使用JDK8和lambda(java.util.stream.streams.zip)压缩流

使用JDK8和lambda(java.util.stream.streams.zip)压缩流,lambda,functional-programming,java-8,lazy-evaluation,java-stream,Lambda,Functional Programming,Java 8,Lazy Evaluation,Java Stream,在使用lambda b93的JDK 8中,有一个类可用于压缩流(教程中对此进行了说明)。此功能: 创建一个惰性的顺序组合流,其元素是 两个流元素组合的结果 然而,在b98中,这种情况已经消失。实际上,Streams类在中甚至不可访问 此功能是否已移动,如果已移动,如何使用b98简洁地压缩流? 我想到的应用程序是,我在 static boolean every(集合c1、集合c2、双预测pred) static T find(集合c1、集合c2、双预测pred) 代码相当详细的函数(不使用b9

在使用lambda b93的JDK 8中,有一个类可用于压缩流(教程中对此进行了说明)。此功能:

创建一个惰性的顺序组合流,其元素是 两个流元素组合的结果

然而,在b98中,这种情况已经消失。实际上,
Streams
类在中甚至不可访问

此功能是否已移动,如果已移动,如何使用b98简洁地压缩流?

我想到的应用程序是,我在

  • static boolean every(集合c1、集合c2、双预测pred)
  • static T find(集合c1、集合c2、双预测pred)

代码相当详细的函数(不使用b98中的功能)。

Lazy Seq库提供zip功能


该库深受scala.collection.immutable.Stream的启发,旨在提供不可变、线程安全且易于使用的惰性序列实现,可能是无限的。

您提到的类的方法已移动到
接口本身,以支持默认方法。但似乎
zip
方法已被删除。可能是因为不清楚不同大小的流的默认行为应该是什么。但实现所需的行为是直截了当的:

static <T> boolean every(
  Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred) {
    Iterator<T> it=c2.iterator();
    return c1.stream().allMatch(x->!it.hasNext()||pred.test(x, it.next()));
}
static <T> T find(Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred) {
    Iterator<T> it=c2.iterator();
    return c1.stream().filter(x->it.hasNext()&&pred.test(x, it.next()))
      .findFirst().orElse(null);
}
静态布尔值(
集合c1、集合c2、双预测pred){
迭代器it=c2.Iterator();
返回c1.stream().allMatch(x->!it.hasNext()| | pred.test(x,it.next());
}
静态T查找(集合c1、集合c2、双预测pred){
迭代器it=c2.Iterator();
返回c1.stream().filter(x->it.hasNext()&&pred.test(x,it.next())
.findFirst().orElse(null);
}

我也需要这个,所以我只是从b93获取了源代码,并将其放在一个“util”类中。为了使用当前的API,我不得不稍微修改它

以下是工作代码供参考(自担风险):


publicstaticstreamzip(Streamzip)是由提供的函数之一

streamA=Stream.of(“A”、“B”、“C”);
溪流B=溪流(“苹果”、“香蕉”、“胡萝卜”、“甜甜圈”);
List zip=StreamUtils.zip(streamA,
streamB,
(a,b)->a+“代表”+b)
.collect(Collectors.toList());
断言(拉链,
包含(“A代表苹果”,“B代表香蕉”,“C代表胡萝卜”);
公共类元组{
私人终审法院反对1;
私人最终反对意见2;
公共元组(S对象1,T对象2){
this.object1=object1;
this.object2=object2;
}
public的getObject1(){
返回对象1;
}
公共T getObject2(){
返回对象2;
}
}
公共类StreamUtils{
私有StreamUtils(){
}
公共静态流zipWithIndex(流){
Stream integerStream=IntStream.range(0,Integer.MAX_值).boxed();
迭代器integerIterator=integerStream.Iterator();
返回stream.map(x->newtuple(integerIterator.next(),x));
}
}

使用JDK8和lambda()压缩两个流

publicstaticstreamzip(streamstreama、streamstreamb、双功能拉链){
final Iterator Iterator=streamA.Iterator();
final Iterator iteratorB=streamB.Iterator();
final Iterator Iterator=new Iterator(){
@凌驾
公共布尔hasNext(){
返回iteratorA.hasNext()&&iteratorB.hasNext();
}
@凌驾
公共C next(){
返回zippers.apply(iteratorA.next(),iteratorB.next());
}
};
最终布尔并行=streamA.isParallel()| | streamB.isParallel();
返回iteratorofinitestream(iteratorC,parallel);
}
公共静态流迭代器of initestream(迭代器迭代器,布尔并行){
最终Iterable Iterable=()->迭代器;
返回StreamSupport.stream(iterable.spliterator(),parallel);
}
我参与的AOL也提供了压缩功能,既可以通过实现反应流接口ReactiveSeq的,也可以通过StreamUtils提供压缩功能,后者通过标准Java流的静态方法提供了大部分相同的功能

 List<Tuple2<Integer,Integer>> list =  ReactiveSeq.of(1,2,3,4,5,6)
                                                  .zip(Stream.of(100,200,300,400));


  List<Tuple2<Integer,Integer>> list = StreamUtils.zip(Stream.of(1,2,3,4,5,6),
                                                  Stream.of(100,200,300,400));
甚至能够将一个流中的每个项目与另一个流中的每个项目配对

   ReactiveSeq.of("a","b","c")
              .forEach2(str->Stream.of(str+"!","2"), a->b->a+"_"+b);

   //ReactiveSeq("a_a!","a_2","b_b!","b_2","c_c!","c2")

这太棒了。我不得不将两条流压缩成一张地图,其中一条流是键,另一条流是值

Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB  = Stream.of("Apple", "Banana", "Carrot", "Doughnut");    
final Stream<Map.Entry<String, String>> s = StreamUtils.zip(streamA,
                    streamB,
                    (a, b) -> {
                        final Map.Entry<String, String> entry = new AbstractMap.SimpleEntry<String, String>(a, b);
                        return entry;
                    });

System.out.println(s.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
streamA=Stream.of(“A”、“B”、“C”);
溪流B=溪流(“苹果”、“香蕉”、“胡萝卜”、“甜甜圈”);
最终流s=StreamUtils.zip(streamA,
streamB,
(a、b)->{
final Map.Entry=新的AbstractMap.SimpleEntry(a,b);
返回条目;
});
System.out.println(s.collect(Collectors.toMap(e->e.getKey(),e->e.getValue());
输出:
{A=Apple,B=Banana,C=Carrot}

由于我无法想象压缩在除索引集合(列表)之外的集合上的任何用途,而且我非常喜欢简单,因此这将是我的解决方案:

<A,B,C>  Stream<C> zipped(List<A> lista, List<B> listb, BiFunction<A,B,C> zipper){
     int shortestLength = Math.min(lista.size(),listb.size());
     return IntStream.range(0,shortestLength).mapToObj( i -> {
          return zipper.apply(lista.get(i), listb.get(i));
     });        
}
流压缩(列表A、列表B、双功能拉链){
int shortestLength=Math.min(lista.size(),listb.size());
返回IntStream.range(0,最短长度).mapToObj(i->{
返回拉链。应用(lista.get(i),listb.get(i));
});        
}

如果您的项目中有番石榴,您可以使用该方法(在番石榴21中添加):

返回一个流,其中每个元素都是将streamA和streamB中每个元素的对应元素传递给函数的结果。结果流的长度仅与两个输入流中的较短者相同;如果一个流较长,则其额外元素
 List<Tuple2<Integer,Integer>> list =  ReactiveSeq.of(1,2,3,4,5,6)
                                                  .zip(Stream.of(100,200,300,400));


  List<Tuple2<Integer,Integer>> list = StreamUtils.zip(Stream.of(1,2,3,4,5,6),
                                                  Stream.of(100,200,300,400));
   ReactiveSeq.of("a","b","c")
              .ap3(this::concat)
              .ap(of("1","2","3"))
              .ap(of(".","?","!"))
              .toList();

   //List("a1.","b2?","c3!");

   private String concat(String a, String b, String c){
    return a+b+c;
   }
   ReactiveSeq.of("a","b","c")
              .forEach2(str->Stream.of(str+"!","2"), a->b->a+"_"+b);

   //ReactiveSeq("a_a!","a_2","b_b!","b_2","c_c!","c2")
Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB  = Stream.of("Apple", "Banana", "Carrot", "Doughnut");    
final Stream<Map.Entry<String, String>> s = StreamUtils.zip(streamA,
                    streamB,
                    (a, b) -> {
                        final Map.Entry<String, String> entry = new AbstractMap.SimpleEntry<String, String>(a, b);
                        return entry;
                    });

System.out.println(s.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
<A,B,C>  Stream<C> zipped(List<A> lista, List<B> listb, BiFunction<A,B,C> zipper){
     int shortestLength = Math.min(lista.size(),listb.size());
     return IntStream.range(0,shortestLength).mapToObj( i -> {
          return zipper.apply(lista.get(i), listb.get(i));
     });        
}
 public class Streams {
     ...

     public static <A, B, R> Stream<R> zip(Stream<A> streamA,
             Stream<B> streamB, BiFunction<? super A, ? super B, R> function) {
         ...
     }
 }
public static <L, R, T> Stream<T> zip(Stream<L> leftStream, Stream<R> rightStream, BiFunction<L, R, T> combiner) {
    Spliterator<L> lefts = leftStream.spliterator();
    Spliterator<R> rights = rightStream.spliterator();
    return StreamSupport.stream(new AbstractSpliterator<T>(Long.min(lefts.estimateSize(), rights.estimateSize()), lefts.characteristics() & rights.characteristics()) {
        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            return lefts.tryAdvance(left->rights.tryAdvance(right->action.accept(combiner.apply(left, right))));
        }
    }, leftStream.isParallel() || rightStream.isParallel());
}
StreamEx<String> givenNames = StreamEx.of("Leo", "Fyodor")
StreamEx<String> familyNames = StreamEx.of("Tolstoy", "Dostoevsky")
StreamEx<String> fullNames = givenNames.zipWith(familyNames, (gn, fn) -> gn + " " + fn);

fullNames.forEach(System.out::println);  // prints: "Leo Tolstoy\nFyodor Dostoevsky\n"
final Map<String, String> result = 
    Streams.zip(
        collection1.stream(), 
        collection2.stream(), 
        AbstractMap.SimpleEntry::new)
    .collect(Collectors.toMap(e -> e.getKey(), e  -> e.getValue()));
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Stream;

class StreamUtils {
    static <ARG1, ARG2, RESULT> Stream<RESULT> zip(
            Stream<ARG1> s1,
            Stream<ARG2> s2,
            BiFunction<ARG1, ARG2, RESULT> combiner) {
        final var i2 = s2.iterator();
        return s1.map(x1 -> i2.hasNext() ? combiner.apply(x1, i2.next()) : null)
                .takeWhile(Objects::nonNull);
    }
}
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")
    <ARG1, ARG2, RESULT>
    void shouldZip(
            String testName,
            Stream<ARG1> s1,
            Stream<ARG2> s2,
            BiFunction<ARG1, ARG2, RESULT> combiner,
            Stream<RESULT> expected) {
        var actual = StreamUtils.zip(s1, s2, combiner);

        assertEquals(
                expected.collect(Collectors.toList()),
                actual.collect(Collectors.toList()),
                testName);
    }

    private static Stream<Arguments> shouldZipTestCases() {
        return Stream.of(
                Arguments.of(
                        "Two empty streams",
                        Stream.empty(),
                        Stream.empty(),
                        (BiFunction<Object, Object, Object>) StreamUtilsTest::combine,
                        Stream.empty()),
                Arguments.of(
                        "One singleton and one empty stream",
                        Stream.of(1),
                        Stream.empty(),
                        (BiFunction<Object, Object, Object>) StreamUtilsTest::combine,
                        Stream.empty()),
                Arguments.of(
                        "One empty and one singleton stream",
                        Stream.empty(),
                        Stream.of(1),
                        (BiFunction<Object, Object, Object>) StreamUtilsTest::combine,
                        Stream.empty()),
                Arguments.of(
                        "Two singleton streams",
                        Stream.of("blah"),
                        Stream.of(1),
                        (BiFunction<Object, Object, Object>) StreamUtilsTest::combine,
                        Stream.of(pair("blah", 1))),
                Arguments.of(
                        "One singleton, one multiple stream",
                        Stream.of("blob"),
                        Stream.of(2, 3),
                        (BiFunction<Object, Object, Object>) StreamUtilsTest::combine,
                        Stream.of(pair("blob", 2))),
                Arguments.of(
                        "One multiple, one singleton stream",
                        Stream.of("foo", "bar"),
                        Stream.of(4),
                        (BiFunction<Object, Object, Object>) StreamUtilsTest::combine,
                        Stream.of(pair("foo", 4))),
                Arguments.of(
                        "Two multiple streams",
                        Stream.of("nine", "eleven"),
                        Stream.of(10, 12),
                        (BiFunction<Object, Object, Object>) StreamUtilsTest::combine,
                        Stream.of(pair("nine", 10), pair("eleven", 12)))
        );
    }

    private static List<Object> pair(Object o1, Object o2) {
        return List.of(o1, o2);
    }

    static private <T1, T2> List<Object> 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);
    }
}