Last active
August 29, 2015 14:21
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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