Skip to content

Instantly share code, notes, and snippets.

@diversit
Last active August 29, 2015 14:21
Show Gist options
  • Select an option

  • Save diversit/7494cd45e61eaa4cf027 to your computer and use it in GitHub Desktop.

Select an option

Save diversit/7494cd45e61eaa4cf027 to your computer and use it in GitHub Desktop.
Stream utils to mkString and flatten streams and options (via Collector!) with test cases
/**
* Some utility methods to work more easily with Java 8's Streams.
*/
public class StreamUtils {
private StreamUtils() {}
/**
* @param coll Collection of items.
* @return A String of all items in the collections seperated by a ',' using 'toString' to create
* String representation of an item.
*/
public static String mkString(Collection<?> coll) {
return mkString(coll, Object::toString);
}
/**
* @param coll Collection of items.
* @param fToString Function to create a String representation of an item.
* @return A String of all items in the collections seperated by a ',' using 'toString' to create
* String representation of an item.
*/
public static <T> String mkString(Collection<T> coll, Function<T, String> fToString) {
return mkString(coll, fToString, ",");
}
/**
* @param coll Collection of items.
* @param fToString Function to create a String representation of an item.
* @param seperator Seperator between items.
* @return A String of all items in the collections seperated by a ',' using 'toString' to create
* String representation of an item.
*/
public static <T> String mkString(Collection<T> coll, Function<T, String> fToString, String seperator) {
return coll.stream().map(item -> fToString.apply(item)).collect(Collectors.joining(seperator));
}
/**
* Easily map over a Map like you would do a {@link Map#forEach(BiConsumer)}.
* @param map {@link Map} over which to map item entries.
* @param mapper Function to map key -> value entries to something of type R.
* @param <K> Key type.
* @param <V> Value type.
* @param <R> Result type.
* @return The result of the {@link Stream#map(Function)} operation.
*/
public static <K,V, R> Stream<R> mapMap(Map<K,V> map, BiFunction<K,V,R> mapper) {
return map.entrySet().stream().map(entry -> mapper.apply(entry.getKey(), entry.getValue()));
}
/**
* Flatten a stream of streams.
*
* @param streamOfStreams Streams to flatten into a single stream.
* @param <T> Type of stream.
* @return A single stream of type T's.
*/
public static <T> Stream<T> flatten(Stream<Stream<T>> streamOfStreams) {
return streamOfStreams.flatMap(Function.<Stream<T>>identity());
}
/**
* Flatten a {@link Stream} of {@link Optional}s.
*
* @param streamOfOptions The stream to flatten.
* @param <T> Type of Optional.
* @return A {@link Stream} of T's where all empty Optionals have been removed.
*/
public static <T> Stream<T> flattenOpts(Stream<Optional<T>> streamOfOptions) {
return streamOfOptions.flatMap(StreamUtils::streamOpt);
}
/**
* Change an {@link Optional} into a {@link Stream}.
*
* @param o Optional value
* @param <T>
* @return A {@link Stream} of with 1 value or empty.
*/
public static <T> Stream<T> streamOpt(Optional<T> o) {
return o.map(Stream::of).orElse(Stream.<T>empty());
}
public static class Collectors {
public static <T> Collector<Stream<T>, ?, Stream<T>> flatten() {
return Collector.of(
() -> Stream.<T>builder(),
(builder, stream) -> stream.forEach(item -> builder.add(item)),
(b1, b2) -> { b2.build().forEach(item -> b1.add(item)); return b1; },
builder -> builder.build(),
Collector.Characteristics.UNORDERED
);
}
public static <T> Collector<Optional<T>, ?, Stream<T>> flattenOpt() {
return Collector.of(
() -> Stream.<T>builder(),
(builder, opt) -> opt.ifPresent(v -> builder.add(v)),
(b1, b2) -> { b2.build().forEach(item -> b1.add(item)); return b1; },
builder -> builder.build(),
Collector.Characteristics.UNORDERED
);
}
}
}
/* Test cases */
package com.philips.flexc.api.protocol.hue.utils;
import org.testng.annotations.Test;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.philips.flexc.api.protocol.hue.utils.StreamUtils.mapMap;
import static com.philips.flexc.api.protocol.hue.utils.StreamUtils.mkString;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
public class StreamUtilsTest {
@Test
public void testMkString1() {
String s = mkString(Arrays.asList(1, 2, 3));
assertThat(s, is("1,2,3"));
}
@Test
public void testMkString2() {
String s = mkString(Arrays.asList("1", "2", "3"), elem -> "" + elem.hashCode());
assertThat(s, is("49,50,51"));
}
@Test
public void testMkString3() {
String s = mkString(Arrays.asList("1", "2", "3"), elem -> "" + elem.hashCode(), ":");
assertThat(s, is("49:50:51"));
}
@Test
public void testMapMap() {
Map<Integer, Integer> testMap = new HashMap() {{
put(1, 1);
put(3, 3);
}};
List<Integer> keys = mapMap(testMap, (key, value) -> key)
.collect(Collectors.toList());
assertThat(keys, hasItems(1, 3));
}
@Test
public void testCollectorFlatten() {
List<Integer> ints = Stream.of(Stream.of(1, 2, 3), Stream.of(4, 5, 6))
.collect(StreamUtils.Collectors.flatten())
.collect(Collectors.toList());
assertThat(ints, hasItems(1,2,3,4,5,6));
}
@Test
public void testCollectorFlattenOpts() {
List<Optional<Integer>> optInts = Arrays.asList(
Optional.of(1),
Optional.<Integer>empty(),
Optional.of(3)
);
List<Integer> ints = optInts.stream()
.collect(StreamUtils.Collectors.flattenOpt())
.collect(Collectors.toList());
assertThat(ints, hasItems(1, 3));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment