使用带有lambda的JDK8压缩流(java.util.stream.Streams.zip)

art*_*lla 141 lambda functional-programming lazy-evaluation java-8 java-stream

在带有lambda b93的JDK 8中,b93中有一个类java.util.stream.Streams.zip可用于压缩流(这在教程Exploring Java8 Lambdas中说明.Dhananjay Nene的第1部分).这个功能:

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

然而在b98中,这已经消失了.事实上,在b98Streams中的java.util.stream中甚至无法访问该类.

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

我想到的应用程序是在Shen的这个java实现中,我在其中替换了zip中的zip功能

  • static <T> boolean every(Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred)
  • static <T> T find(Collection<T> c1, Collection<T> c2, BiPredicate<T, T> pred)

具有相当冗长代码的函数(不使用b98中的功能).

sik*_*iki 73

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

这里是参考工作代码(自担风险......):

public static<A, B, C> Stream<C> zip(Stream<? extends A> a,
                                     Stream<? extends B> b,
                                     BiFunction<? super A, ? super B, ? extends C> zipper) {
    Objects.requireNonNull(zipper);
    Spliterator<? extends A> aSpliterator = Objects.requireNonNull(a).spliterator();
    Spliterator<? extends B> bSpliterator = Objects.requireNonNull(b).spliterator();

    // Zipping looses DISTINCT and SORTED characteristics
    int characteristics = aSpliterator.characteristics() & bSpliterator.characteristics() &
            ~(Spliterator.DISTINCT | Spliterator.SORTED);

    long zipSize = ((characteristics & Spliterator.SIZED) != 0)
            ? Math.min(aSpliterator.getExactSizeIfKnown(), bSpliterator.getExactSizeIfKnown())
            : -1;

    Iterator<A> aIterator = Spliterators.iterator(aSpliterator);
    Iterator<B> bIterator = Spliterators.iterator(bSpliterator);
    Iterator<C> cIterator = new Iterator<C>() {
        @Override
        public boolean hasNext() {
            return aIterator.hasNext() && bIterator.hasNext();
        }

        @Override
        public C next() {
            return zipper.apply(aIterator.next(), bIterator.next());
        }
    };

    Spliterator<C> split = Spliterators.spliterator(cIterator, zipSize, characteristics);
    return (a.isParallel() || b.isParallel())
           ? StreamSupport.stream(split, true)
           : StreamSupport.stream(split, false);
}
Run Code Online (Sandbox Code Playgroud)

  • 我不这么认为.两个流必须是"SIZED"才能使此实现工作.它实际上取决于你如何定义压缩.例如,您是否能够压缩两个不同大小的流?那么结果流会是什么样的呢?我相信这就是为什么API中实际省略了这个功能的原因.有很多方法可以做到这一点,用户可以决定哪种行为应该是"正确的"行为.你会丢弃较长流中的元素或填充较短的列表吗?如果是这样,具有什么价值? (4认同)

Dom*_*Fox 41

zip是protonpack库提供的功能之一.

Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB  = Stream.of("Apple", "Banana", "Carrot", "Doughnut");

List<String> zipped = StreamUtils.zip(streamA,
                                      streamB,
                                      (a, b) -> a + " is for " + b)
                                 .collect(Collectors.toList());

assertThat(zipped,
           contains("A is for Apple", "B is for Banana", "C is for Carrot"));
Run Code Online (Sandbox Code Playgroud)


Zhe*_*lov 30

如果你的项目中有Guava,你可以使用Streams.zip方法(在Guava 21中添加):

返回一个流,其中每个元素是将streamA和streamB中的每个元素的相应元素传递给function的结果.结果流只会与两个输入流中较短的一个一样长; 如果一个流更长,其额外元素将被忽略.得到的流不能有效地分裂.这可能会损害并行性能.

 public class Streams {
     ...

     public static <A, B, R> Stream<R> zip(Stream<A> streamA,
             Stream<B> streamB, BiFunction<? super A, ? super B, R> function) {
         ...
     }
 }
Run Code Online (Sandbox Code Playgroud)


Kar*_*ról 25

使用带有lambda(gist)的JDK8压缩两个流.

public static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
    final Iterator<A> iteratorA = streamA.iterator();
    final Iterator<B> iteratorB = streamB.iterator();
    final Iterator<C> iteratorC = new Iterator<C>() {
        @Override
        public boolean hasNext() {
            return iteratorA.hasNext() && iteratorB.hasNext();
        }

        @Override
        public C next() {
            return zipper.apply(iteratorA.next(), iteratorB.next());
        }
    };
    final boolean parallel = streamA.isParallel() || streamB.isParallel();
    return iteratorToFiniteStream(iteratorC, parallel);
}

public static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
    final Iterable<T> iterable = () -> iterator;
    return StreamSupport.stream(iterable.spliterator(), parallel);
}
Run Code Online (Sandbox Code Playgroud)

  • 不错的解决方案和(相对)紧凑!要求您将 `import java.util.function.*;` 和 `import java.util.stream.*;` 放在文件的顶部。 (2认同)
  • 这么多无用的包装器:这里是`() -&gt; iterator`,这里又是:`iterable.spliterator()`。为什么不直接实现“Spliterator”而不是“Iterator”?检查@Doradus 答案 /sf/answers/3236116341/ (2认同)

Raf*_*ael 18

由于我不能设想在索引(列表)以外的集合上使用压缩,而且我是简单的忠实粉丝,这将是我的解决方案:

<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));
     });        
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为`mapToObject`应该是`mapToObj`。 (2认同)
  • 如果列表不是“RandomAccess”(例如在链接列表上),这将非常慢 (2认同)

Hol*_*ger 10

您提到的类的方法已被移动到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);
}
Run Code Online (Sandbox Code Playgroud)

  • @Andreas:这里的解决方案都不支持并行处理.由于我的方法不返回流,因此它们确保流不并行运行.类似地,接受的答案的代码返回一个流,该流可以转换为并行但实际上不会并行执行任何操作.也就是说,有条不紊的谓词不鼓励但不违反合同.如果确保状态更新是线程安全的,它们甚至可能在并行上下文中使用.在某些情况下,它们是不可避免的,例如将流转换为不同的是一个有状态的谓词*本身*. (2认同)
  • @Andreas:你可能猜到为什么这些操作已经从Java API中删除了...... (2认同)

Dan*_*rza 9

使用最新的番石榴库(对于Streams班级),您应该能够做到

final Map<String, String> result = 
    Streams.zip(
        collection1.stream(), 
        collection2.stream(), 
        AbstractMap.SimpleEntry::new)
    .collect(Collectors.toMap(e -> e.getKey(), e  -> e.getValue()));
Run Code Online (Sandbox Code Playgroud)


Dor*_*dus 7

我谦虚地建议这种实现。生成的流被截断为两个输入流中较短的一个。

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());
}
Run Code Online (Sandbox Code Playgroud)


Nic*_*kis 6

Lazy-Seq库提供zip功能.

https://github.com/nurkiewicz/LazySeq

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