Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip)

前端 未结 14 1369
旧巷少年郎
旧巷少年郎 2020-11-21 23:24

In JDK 8 with lambda b93 there was a class java.util.stream.Streams.zip in b93 which could be used to zip streams (this is illustrated in the tutorial Exploring Java8 Lambda

14条回答
  •  一生所求
    2020-11-21 23:44

    Would this work for you? It's a short function, which lazily evaluates over the streams it's zipping, so you can supply it with infinite streams (it doesn't need to take the size of the streams being zipped).

    If the streams are finite it stops as soon as one of the streams runs out of elements.

    import java.util.Objects;
    import java.util.function.BiFunction;
    import java.util.stream.Stream;
    
    class StreamUtils {
        static  Stream zip(
                Stream s1,
                Stream s2,
                BiFunction combiner) {
            final var i2 = s2.iterator();
            return s1.map(x1 -> i2.hasNext() ? combiner.apply(x1, i2.next()) : null)
                    .takeWhile(Objects::nonNull);
        }
    }
    

    Here is some unit test code (much longer than the code itself!)

    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.Arguments;
    import org.junit.jupiter.params.provider.MethodSource;
    
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.function.BiFunction;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    
    class StreamUtilsTest {
        @ParameterizedTest
        @MethodSource("shouldZipTestCases")
        
        void shouldZip(
                String testName,
                Stream s1,
                Stream s2,
                BiFunction combiner,
                Stream expected) {
            var actual = StreamUtils.zip(s1, s2, combiner);
    
            assertEquals(
                    expected.collect(Collectors.toList()),
                    actual.collect(Collectors.toList()),
                    testName);
        }
    
        private static Stream shouldZipTestCases() {
            return Stream.of(
                    Arguments.of(
                            "Two empty streams",
                            Stream.empty(),
                            Stream.empty(),
                            (BiFunction) StreamUtilsTest::combine,
                            Stream.empty()),
                    Arguments.of(
                            "One singleton and one empty stream",
                            Stream.of(1),
                            Stream.empty(),
                            (BiFunction) StreamUtilsTest::combine,
                            Stream.empty()),
                    Arguments.of(
                            "One empty and one singleton stream",
                            Stream.empty(),
                            Stream.of(1),
                            (BiFunction) StreamUtilsTest::combine,
                            Stream.empty()),
                    Arguments.of(
                            "Two singleton streams",
                            Stream.of("blah"),
                            Stream.of(1),
                            (BiFunction) StreamUtilsTest::combine,
                            Stream.of(pair("blah", 1))),
                    Arguments.of(
                            "One singleton, one multiple stream",
                            Stream.of("blob"),
                            Stream.of(2, 3),
                            (BiFunction) StreamUtilsTest::combine,
                            Stream.of(pair("blob", 2))),
                    Arguments.of(
                            "One multiple, one singleton stream",
                            Stream.of("foo", "bar"),
                            Stream.of(4),
                            (BiFunction) StreamUtilsTest::combine,
                            Stream.of(pair("foo", 4))),
                    Arguments.of(
                            "Two multiple streams",
                            Stream.of("nine", "eleven"),
                            Stream.of(10, 12),
                            (BiFunction) StreamUtilsTest::combine,
                            Stream.of(pair("nine", 10), pair("eleven", 12)))
            );
        }
    
        private static List pair(Object o1, Object o2) {
            return List.of(o1, o2);
        }
    
        static private  List combine(T1 o1, T2 o2) {
            return List.of(o1, o2);
        }
    
        @Test
        void shouldLazilyEvaluateInZip() {
            final var a = new AtomicInteger();
            final var b = new AtomicInteger();
            final var zipped = StreamUtils.zip(
                    Stream.generate(a::incrementAndGet),
                    Stream.generate(b::decrementAndGet),
                    (xa, xb) -> xb + 3 * xa);
    
            assertEquals(0, a.get(), "Should not have evaluated a at start");
            assertEquals(0, b.get(), "Should not have evaluated b at start");
    
            final var takeTwo = zipped.limit(2);
    
            assertEquals(0, a.get(), "Should not have evaluated a at take");
            assertEquals(0, b.get(), "Should not have evaluated b at take");
    
            final var list = takeTwo.collect(Collectors.toList());
    
            assertEquals(2, a.get(), "Should have evaluated a after collect");
            assertEquals(-2, b.get(), "Should have evaluated b after collect");
            assertEquals(List.of(2, 4), list);
        }
    }
    
        

    提交回复
    热议问题