Skip to content

Commit

Permalink
Enhance MapStream and Guavate (OpenGamma#1783)
Browse files Browse the repository at this point in the history
* Provide zipWithIndex on MapStream

Use Guava's implementation as it does the spliterator properly

* Add MapStream.toListMultimap()

Since the keys are not necessarily unique, this makes sense

* Add public method for creating Map.Entry

Static method avoids generics pain
  • Loading branch information
jodastephen committed Aug 20, 2018
1 parent 5c0a680 commit 2cdcbfe
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static java.util.stream.Collectors.collectingAndThen;

import java.time.Duration;
import java.util.AbstractMap;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -125,6 +126,18 @@ public static <T> Optional<T> firstNonEmpty(Optional<? extends T>... optionals)
return Optional.empty();
}

//-------------------------------------------------------------------------
/**
* Creates a single {@code Map.Entry}.
*
* @param key the key
* @param value the value
* @return the map entry
*/
public static <K, V> Map.Entry<K, V> entry(K key, V value) {
return new AbstractMap.SimpleImmutableEntry<>(key, value);
}

//-------------------------------------------------------------------------
/**
* Converts an iterable to a serial stream.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
*/
package com.opengamma.strata.collect;

import java.util.AbstractMap;
import static com.opengamma.strata.collect.Guavate.entry;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
Expand All @@ -29,7 +30,9 @@
import java.util.stream.LongStream;
import java.util.stream.Stream;

import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;

/**
* A stream implementation based on {@code Map.Entry}.
Expand Down Expand Up @@ -128,6 +131,19 @@ public static <T, K, V> MapStream<K, V> of(
return new MapStream<>(stream.map(item -> entry(keyFunction.apply(item), valueFunction.apply(item))));
}

/**
* Returns a stream of map entries where each key is the index of the value in the original stream.
*
* @param <V> the value type
* @param stream the stream of values
* @return a stream of map entries derived from the stream
*/
public static <V> MapStream<Integer, V> zipWithIndex(Stream<V> stream) {
Stream<Map.Entry<Integer, V>> zipped =
Streams.mapWithIndex(stream, (value, index) -> entry(Math.toIntExact(index), value));
return new MapStream<Integer, V>(zipped);
}

/**
* Returns an empty map stream.
*
Expand Down Expand Up @@ -320,6 +336,15 @@ public ImmutableMap<K, V> toMap(BiFunction<? super V, ? super V, ? extends V> me
return underlying.collect(Guavate.toImmutableMap(e -> e.getKey(), e -> e.getValue(), mergeFn));
}

/**
* Returns an immutable list multimap built from the entries in the stream.
*
* @return an immutable list multimap built from the entries in the stream
*/
public ImmutableListMultimap<K, V> toListMultimap() {
return underlying.collect(Guavate.toImmutableListMultimap(e -> e.getKey(), e -> e.getValue()));
}

/**
* Performs an action for each entry in the stream, passing the key and value to the action.
*
Expand Down Expand Up @@ -535,10 +560,6 @@ public void close() {
}

//-------------------------------------------------------------------------
private static <K, V> Map.Entry<K, V> entry(K k, V v) {
return new AbstractMap.SimpleImmutableEntry<>(k, v);
}

private static <K, V> MapStream<K, V> wrap(Stream<Map.Entry<K, V>> underlying) {
return new MapStream<>(underlying);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,13 @@ public void test_pairsToImmutableMap() {
assertEquals(output, expected);
}

//-------------------------------------------------------------------------
public void test_entry() {
Map.Entry<String, Integer> test = Guavate.entry("A", 1);
assertEquals(test.getKey(), "A");
assertEquals(test.getValue(), (Integer) 1);
}

//-------------------------------------------------------------------------
public void test_combineFuturesAsList() {
CompletableFuture<String> future1 = new CompletableFuture<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import org.testng.annotations.Test;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;

@Test
public class MapStreamTest {
Expand Down Expand Up @@ -137,6 +139,14 @@ public void ofStream_2arg() {
assertThat(result).isEqualTo(expected);
}

public void zipWithIndex() {
Stream<String> letters = Stream.of("a", "b", "c");
Map<Integer, String> expected = ImmutableMap.of(0, "a", 1, "b", 2, "c");
Map<Integer, String> result = MapStream.zipWithIndex(letters).toMap();
assertThat(result).isEqualTo(expected);
}

//-------------------------------------------------------------------------
public void toMapDuplicateKeys() {
assertThrowsIllegalArg(() -> MapStream.of(map).mapKeys(k -> "key").toMap());
}
Expand All @@ -148,6 +158,13 @@ public void toMapWithMerge() {
assertThat(result).isEqualTo(expected);
}

public void toListMultimap() {
Map<String, Integer> map = ImmutableMap.of("a", 1, "aa", 2, "b", 10, "bb", 20, "c", 1);
ListMultimap<String, Integer> expected = ImmutableListMultimap.of("a", 1, "a", 2, "b", 10, "b", 20, "c", 1);
ListMultimap<String, Integer> result = MapStream.of(map).mapKeys(s -> s.substring(0, 1)).toListMultimap();
assertThat(result).isEqualTo(expected);
}

public void coverage() {
MapStream.empty()
.filter(e -> false)
Expand Down

0 comments on commit 2cdcbfe

Please sign in to comment.