Сжатие потоков с помощью JDK8 с лямбда (java.утиль.поток.Потоки.застежка-молния)
в JDK 8 с лямбда b93 был класс java.утиль.поток.Потоки.zip в b93 который может быть использован для zip потоков (это показано в учебнике Изучение Java8 Лямбды. Часть 1 по Dhananjay Nene). Эта функция :
создает ленивый и последовательный комбинированный поток, элементами которого являются результат объединения элементов двух потоков.
однако в b98 это исчезло. Фактически Streams
класс даже не доступен в java.утиль.поток в b98.
была ли перемещена эта функциональность, и если да, то как мне сжато использовать потоки zip с помощью b98?
приложение, которое я имею в виду в этой реализации java Shen, где я заменил функциональность 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).
13 ответов:
мне это тоже было нужно, поэтому я просто взял исходный код из 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); }
zip-это одна из функций, предоставляемых библиотека протонного пакета.
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"));
Если у вас есть гуаву в вашем проекте, вы можете использовать потоки.молнии метод (был добавлен в Guava 21):
возвращает поток, в котором каждый элемент является результатом передачи соответствующего элемента каждого из streamA и streamB в функцию. Результирующий поток будет таким же длинным, как и более короткий из двух входных потоков; если один поток длиннее, его дополнительные элементы будут проигнорированы. Полученный поток не является эффективно расщепляемым. Это может навредить параллельное исполнение.
public class Streams { ... public static <A, B, R> Stream<R> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<? super A, ? super B, R> function) { ... } }
сжатие двух потоков с помощью 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); }
Так как я не могу представить себе никакого использования молнии на коллекциях, кроме индексированных (списки), и я большой поклонник простоты, это было бы мое решение:
<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).mapToObject( i -> { return zipper.apply(lista.get(i), listb.get(i)); }); }
методы класса, который вы упомянули, были перемещены в
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); }
библиотека Lazy-Seq предоставляет функции zip.
https://github.com/nurkiewicz/LazySeq
эта библиотека сильно вдохновлен
scala.collection.immutable.Stream
и стремится обеспечить неизменяемую, потокобезопасную и простую в использовании реализацию ленивой последовательности, возможно, бесконечную.
public class Tuple<S,T> { private final S object1; private final T object2; public Tuple(S object1, T object2) { this.object1 = object1; this.object2 = object2; } public S getObject1() { return object1; } public T getObject2() { return object2; } } public class StreamUtils { private StreamUtils() { } public static <T> Stream<Tuple<Integer,T>> zipWithIndex(Stream<T> stream) { Stream<Integer> integerStream = IntStream.range(0, Integer.MAX_VALUE).boxed(); Iterator<Integer> integerIterator = integerStream.iterator(); return stream.map(x -> new Tuple<>(integerIterator.next(), x)); } }
АОЛ-х Циклоп-реагировать, в который я вношу свой вклад, также обеспечивает функциональность молнии, как через расширенная реализация трансляция, который также реализует интерфейс reactive-streams 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") .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=Морковь}
Я смиренно предлагаю эту реализацию. Полученный поток усекается до более короткого из двух входных потоков.
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()); }