一般问题:反转流的正确方法是什么?假设我们不知道流包含哪种类型的元素,那么反转任何流的通用方法是什么?
具体问题:
IntStream
提供了范围方法来生成特定范围IntStream.range(-range, 0)
的整数,现在我想要将其反转,从0到负的切换范围将不起作用,我也不能使用{{1 }}
Integer::compare
带有List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
的我将得到此编译器错误
错误:(191,0)ajc:类型
IntStream
中的方法sorted()
不适用于参数(IntStream
)
我在这里错过了什么?
答案 0 :(得分:58)
有关生成反向IntStream
的具体问题,请尝试以下方法:
static IntStream revRange(int from, int to) {
return IntStream.range(from, to)
.map(i -> to - i + from - 1);
}
这可以避免装箱和分拣。
关于如何扭转任何类型的流的一般问题,我不知道那里有一个&#34;正确的&#34;办法。我有几种方法可以想到。两者最终都存储了流元素。我不知道如何在不存储元素的情况下反转流。
第一种方法将元素存储到一个数组中,并以相反的顺序将它们读出来。请注意,由于我们不知道流元素的运行时类型,因此我们无法正确键入数组,需要未经检查的强制转换。
@SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
Object[] temp = input.toArray();
return (Stream<T>) IntStream.range(0, temp.length)
.mapToObj(i -> temp[temp.length - i - 1]);
}
另一种技术使用收集器将项目累积到反向列表中。这会在ArrayList
个对象的前面进行大量插入,因此需要进行大量复制。
Stream<T> input = ... ;
List<T> output =
input.collect(ArrayList::new,
(list, e) -> list.add(0, e),
(list1, list2) -> list1.addAll(0, list2));
使用某种自定义数据结构编写效率更高的反转收集器可能是可能的。
更新2016-01-29
由于这个问题最近得到了一些关注,我想我应该更新我的答案来解决问题,插入ArrayList
的前面。对于大量元素,这将是非常低效的,需要O(N ^ 2)复制。
最好使用ArrayDeque
代替,它有效地支持前面的插入。一个小皱纹是我们不能使用Stream.collect()
的三个arg形式;它要求第二个arg的内容合并到第一个arg中,并且没有&#34; add-all-at-front&#34; Deque
上的批量操作。相反,我们使用addAll()
将第一个arg的内容追加到第二个arg的末尾,然后我们返回第二个arg。这需要使用Collector.of()
工厂方法。
完整的代码是:
Deque<String> output =
input.collect(Collector.of(
ArrayDeque::new,
(deq, t) -> deq.addFirst(t),
(d1, d2) -> { d2.addAll(d1); return d2; }));
结果是Deque
而不是List
,但这不应该是一个问题,因为它可以很容易地以现在颠倒的顺序进行迭代或流式传输。< / p>
答案 1 :(得分:33)
优雅的解决方案
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillAfter="true"
android:duration="500" />
答案 2 :(得分:30)
这里的许多解决方案对IntStream
进行排序或反转,但这不必要地需要中间存储。 Stuart Marks's solution是要走的路:
static IntStream revRange(int from, int to) {
return IntStream.range(from, to).map(i -> to - i + from - 1);
}
它也正确处理溢出,通过了这个测试:
@Test
public void testRevRange() {
assertArrayEquals(revRange(0, 5).toArray(), new int[]{4, 3, 2, 1, 0});
assertArrayEquals(revRange(-5, 0).toArray(), new int[]{-1, -2, -3, -4, -5});
assertArrayEquals(revRange(1, 4).toArray(), new int[]{3, 2, 1});
assertArrayEquals(revRange(0, 0).toArray(), new int[0]);
assertArrayEquals(revRange(0, -1).toArray(), new int[0]);
assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE).toArray(), new int[0]);
assertArrayEquals(revRange(MAX_VALUE, MAX_VALUE).toArray(), new int[0]);
assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE + 1).toArray(), new int[]{MIN_VALUE});
assertArrayEquals(revRange(MAX_VALUE - 1, MAX_VALUE).toArray(), new int[]{MAX_VALUE - 1});
}
答案 3 :(得分:28)
一般问题:
Stream不存储任何元素。
因此,如果不将元素存储在某个中间集合中,则无法以相反的顺序迭代元素。
Stream.of("1", "2", "20", "3")
.collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList
.descendingIterator()
.forEachRemaining(System.out::println);
更新:将LinkedList更改为ArrayDeque(更好)see here for details
打印:
3
20
2
1
顺便说一下,使用sort
方法是不正确的,因为它排序,而不是反转(假设流可能有无序元素)
具体问题:
我发现这个简单,容易和直观(复制@Holger评论)
IntStream.iterate(to - 1, i -> i - 1).limit(to - from)
答案 4 :(得分:14)
没有外部lib ...
import java.util.List;
import java.util.Collections;
import java.util.stream.Collector;
public class MyCollectors {
public static <T> Collector<T, ?, List<T>> toListReversed() {
return Collectors.collectingAndThen(Collectors.toList(), l -> {
Collections.reverse(l);
return l;
});
}
}
答案 5 :(得分:14)
如果已实施Comparable<T>
(例如Integer
,String
,Date
),您可以使用Comparator.reverseOrder()
执行此操作。
List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);
答案 6 :(得分:10)
您可以定义自己的收集器,以相反的顺序收集元素:
public static <T> Collector<T, List<T>, List<T>> inReverse() {
return Collector.of(
ArrayList::new,
(l, t) -> l.add(t),
(l, r) -> {l.addAll(r); return l;},
Lists::<T>reverse);
}
并使用它:
stream.collect(inReverse()).forEach(t -> ...)
我以正向顺序使用ArrayList来有效地插入收集项目(在列表的末尾),并使用Guava Lists.reverse有效地提供列表的反向视图,而无需另外复制它。
以下是自定义收集器的一些测试用例:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import org.hamcrest.Matchers;
import org.junit.Test;
import com.google.common.collect.Lists;
public class TestReverseCollector {
private final Object t1 = new Object();
private final Object t2 = new Object();
private final Object t3 = new Object();
private final Object t4 = new Object();
private final Collector<Object, List<Object>, List<Object>> inReverse = inReverse();
private final Supplier<List<Object>> supplier = inReverse.supplier();
private final BiConsumer<List<Object>, Object> accumulator = inReverse.accumulator();
private final Function<List<Object>, List<Object>> finisher = inReverse.finisher();
private final BinaryOperator<List<Object>> combiner = inReverse.combiner();
@Test public void associative() {
final List<Object> a1 = supplier.get();
accumulator.accept(a1, t1);
accumulator.accept(a1, t2);
final List<Object> r1 = finisher.apply(a1);
final List<Object> a2 = supplier.get();
accumulator.accept(a2, t1);
final List<Object> a3 = supplier.get();
accumulator.accept(a3, t2);
final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));
assertThat(r1, Matchers.equalTo(r2));
}
@Test public void identity() {
final List<Object> a1 = supplier.get();
accumulator.accept(a1, t1);
accumulator.accept(a1, t2);
final List<Object> r1 = finisher.apply(a1);
final List<Object> a2 = supplier.get();
accumulator.accept(a2, t1);
accumulator.accept(a2, t2);
final List<Object> r2 = finisher.apply(combiner.apply(a2, supplier.get()));
assertThat(r1, equalTo(r2));
}
@Test public void reversing() throws Exception {
final List<Object> a2 = supplier.get();
accumulator.accept(a2, t1);
accumulator.accept(a2, t2);
final List<Object> a3 = supplier.get();
accumulator.accept(a3, t3);
accumulator.accept(a3, t4);
final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));
assertThat(r2, contains(t4, t3, t2, t1));
}
public static <T> Collector<T, List<T>, List<T>> inReverse() {
return Collector.of(
ArrayList::new,
(l, t) -> l.add(t),
(l, r) -> {l.addAll(r); return l;},
Lists::<T>reverse);
}
}
答案 7 :(得分:9)
cyclops-react StreamUtils有一个反向流方法(javadoc)。
StreamUtils.reverse(Stream.of("1", "2", "20", "3"))
.forEach(System.out::println);
它的工作原理是收集到一个ArrayList,然后利用可以向任一方向迭代的ListIterator类,在列表上向后迭代。
如果您已经有了一个List,那么效率会更高
StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
.forEach(System.out::println);
答案 8 :(得分:6)
以下是我提出的解决方案:
private static final Comparator<Integer> BY_ASCENDING_ORDER = Integer::compare;
private static final Comparator<Integer> BY_DESCENDING_ORDER = BY_ASCENDING_ORDER.reversed();
然后使用那些比较器:
IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...
答案 9 :(得分:5)
如何不这样做:
.sorted(Comparator.reverseOrder())
或.sorted(Collections.reverseOrder())
,因为它只会按降序对元素进行排序。[1, 4, 2, 5, 3]
[5, 4, 3, 2, 1]
["A", "D", "B", "E", "C"]
[E, D, C, B, A]
.sorted((a, b) -> -1)
(末尾说明)正确执行此操作的最简单方法:
List<Integer> list = Arrays.asList(1, 4, 2, 5, 3);
Collections.reverse(list);
System.out.println(list);
输出:
[3, 5, 2, 4, 1]
与String
相同:
List<String> stringList = Arrays.asList("A", "D", "B", "E", "C");
Collections.reverse(stringList);
System.out.println(stringList);
输出:
[C, E, B, D, A]
请勿使用.sorted((a, b) -> -1)
!
它违反了比较器合同,可能仅在某些情况下有效,即。仅在单线程上,而不是并行。
扬基说明:
在我的机器上重现此错误
(a, b) -> -1
违反了Comparator
的合同。这是否有效取决于排序算法的实现。 JVM的下一发行版可能会打破这一点。实际上,我已经可以使用IntStream.range(0, 10000).parallel().boxed().sorted((a, b) -> -1).forEachOrdered(System.out::println);
//Don't use this!!!
List<Integer> list = Arrays.asList(1, 4, 2, 5, 3);
List<Integer> reversedList = list.stream()
.sorted((a, b) -> -1)
.collect(Collectors.toList());
System.out.println(reversedList);
肯定情况下的输出:
[3, 5, 2, 4, 1]
可能以并行流或其他JVM实现输出:
[4, 1, 2, 3, 5]
与String
相同:
//Don't use this!!!
List<String> stringList = Arrays.asList("A", "D", "B", "E", "C");
List<String> reversedStringList = stringList.stream()
.sorted((a, b) -> -1)
.collect(Collectors.toList());
System.out.println(reversedStringList);
肯定情况下的输出:
[C, E, B, D, A]
可能以并行流或其他JVM实现输出:
[A, E, B, D, C]
答案 10 :(得分:4)
我建议使用jOOλ,它是一个很棒的库,可以为Java 8流和lambdas添加许多有用的功能。
然后您可以执行以下操作:
List<Integer> list = Arrays.asList(1,2,3,4);
Seq.seq(list).reverse().forEach(System.out::println)
这很简单。它是一个非常轻量级的库,值得添加到任何Java 8项目中。
答案 11 :(得分:3)
最简单的方法(简单收集 - 支持并行流):
public static <T> Stream<T> reverse(Stream<T> stream) {
return stream
.collect(Collector.of(
() -> new ArrayDeque<T>(),
ArrayDeque::addFirst,
(q1, q2) -> { q2.addAll(q1); return q2; })
)
.stream();
}
高级方式(以持续的方式支持并行流):
public static <T> Stream<T> reverse(Stream<T> stream) {
Objects.requireNonNull(stream, "stream");
class ReverseSpliterator implements Spliterator<T> {
private Spliterator<T> spliterator;
private final Deque<T> deque = new ArrayDeque<>();
private ReverseSpliterator(Spliterator<T> spliterator) {
this.spliterator = spliterator;
}
@Override
@SuppressWarnings({"StatementWithEmptyBody"})
public boolean tryAdvance(Consumer<? super T> action) {
while(spliterator.tryAdvance(deque::addFirst));
if(!deque.isEmpty()) {
action.accept(deque.remove());
return true;
}
return false;
}
@Override
public Spliterator<T> trySplit() {
// After traveling started the spliterator don't contain elements!
Spliterator<T> prev = spliterator.trySplit();
if(prev == null) {
return null;
}
Spliterator<T> me = spliterator;
spliterator = prev;
return new ReverseSpliterator(me);
}
@Override
public long estimateSize() {
return spliterator.estimateSize();
}
@Override
public int characteristics() {
return spliterator.characteristics();
}
@Override
public Comparator<? super T> getComparator() {
Comparator<? super T> comparator = spliterator.getComparator();
return (comparator != null) ? comparator.reversed() : null;
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
// Ensure that tryAdvance is called at least once
if(!deque.isEmpty() || tryAdvance(action)) {
deque.forEach(action);
}
}
}
return StreamSupport.stream(new ReverseSpliterator(stream.spliterator()), stream.isParallel());
}
请注意,您可以快速扩展到其他类型的流(IntStream,...)。
<强>测试强>
// Use parallel if you wish only
revert(Stream.of("One", "Two", "Three", "Four", "Five", "Six").parallel())
.forEachOrdered(System.out::println);
<强>结果:强>
Six
Five
Four
Three
Two
One
附加说明 simplest way
与其他流操作一起使用时({1}}不太有用(收集连接会破坏并行性)。 advance way
没有这个问题,它还保留了流的初始特征,例如SORTED
,因此,它是在反向之后与其他流操作一起使用的方式。
答案 12 :(得分:2)
可以编写一个收集器,以相反的顺序收集元素:
public static <T> Collector<T, ?, Stream<T>> reversed() {
return Collectors.collectingAndThen(Collectors.toList(), list -> {
Collections.reverse(list);
return list.stream();
});
}
并像这样使用它:
Stream.of(1, 2, 3, 4, 5).collect(reversed()).forEach(System.out::println);
原始回答(包含错误 - 它对并行流无法正常工作):
通用流反向方法可能如下所示:
public static <T> Stream<T> reverse(Stream<T> stream) {
LinkedList<T> stack = new LinkedList<>();
stream.forEach(stack::push);
return stack.stream();
}
答案 13 :(得分:2)
这种实用方法怎么样?
public static <T> Stream<T> getReverseStream(List<T> list) {
final ListIterator<T> listIt = list.listIterator(list.size());
final Iterator<T> reverseIterator = new Iterator<T>() {
@Override
public boolean hasNext() {
return listIt.hasPrevious();
}
@Override
public T next() {
return listIt.previous();
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
reverseIterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
似乎可以处理所有没有重复的案例。
答案 14 :(得分:1)
How about reversing the Collection backing the stream prior?
import java.util.Collections;
import java.util.List;
public void reverseTest(List<Integer> sampleCollection) {
Collections.reverse(sampleCollection); // remember this reverses the elements in the list, so if you want the original input collection to remain untouched clone it first.
sampleCollection.stream().forEach(item -> {
// you op here
});
}
答案 15 :(得分:1)
此方法适用于任何Stream,并且符合Java 8:
import requests
from bs4 import BeautifulSoup
url='https://www.worldometers.info/coronavirus/'
response = requests.get(url)
soup = BeautifulSoup(response.content, 'html.parser')
table = soup.findAll('table',{'id':'main_table_countries'})
答案 16 :(得分:1)
不仅仅是Java8,但如果你结合使用番石榴的Lists.reverse()方法,你可以轻松实现这个目标:
List<Integer> list = Arrays.asList(1,2,3,4);
Lists.reverse(list).stream().forEach(System.out::println);
答案 17 :(得分:1)
关于生成反向IntStream
- Java 9 的具体问题,引入了另一种IntStream.iterate(...)
方法,可以很容易地用于以相反的顺序迭代:< / p>
IntStream.iterate(10, x -> x >= 0, x -> x - 1).forEach(System.out::println);
// Out: 10 9 8 7 6 5 4 3 2 1 0
方法说明:
IntStream.iterate(int seed, IntPredicate hasNext, IntUnaryOperator next);
seed
- 初始元素; hasNext
- 一个谓词,用于应用于元素以确定何时
流必须终止; next
- 要应用于前一个元素以生成a的函数
新元素。答案 18 :(得分:1)
回答使用IntStream进行逆转的具体问题,下面对我有用:
function get_shipping_name_by_id( $shipping_id ) {
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
if ( isset( $package['rates'] ) && isset( $package['rates'][ $shipping_id ] ) ) {
$rate = $package['rates'][ $shipping_id ];
/* @var $rate WC_Shipping_Rate */
return $rate->get_label();
}
}
return '';
}
答案 19 :(得分:1)
作为参考,我正在查看同样的问题,我想以相反的顺序加入流元素的字符串值。
itemList = {last,middle,first} =&gt;第一,中间,最后
我开始使用来自 comonad 的collectingAndThen
或 Stuart Marks 的ArrayDeque
收藏家的中间集合,尽管我不是&#39;对中间收集很满意,再次流式传输
itemList.stream()
.map(TheObject::toString)
.collect(Collectors.collectingAndThen(Collectors.toList(),
strings -> {
Collections.reverse(strings);
return strings;
}))
.stream()
.collect(Collector.joining());
所以我重复了Stuart Marks使用Collector.of
工厂的答案,该工厂有一个有趣的 finisher lambda。
itemList.stream()
.collect(Collector.of(StringBuilder::new,
(sb, o) -> sb.insert(0, o),
(r1, r2) -> { r1.insert(0, r2); return r1; },
StringBuilder::toString));
因为在这种情况下流不是并行的,所以组合器并不那么相关,我为了代码一致性而使用insert
但是它无关紧要,因为它取决于哪个首先构建stringbuilder。
我查看了StringJoiner,但它没有insert
方法。
答案 20 :(得分:0)
我就是这样做的。
我不喜欢创建新集合并反向迭代它的想法。
IntStream#map的想法非常简洁,但我更喜欢IntStream#iterate方法,因为我觉得倒计时的想法更好用iterate方法表示,并且更容易理解从后面走到数组前面。
import static java.lang.Math.max;
private static final double EXACT_MATCH = 0d;
public static IntStream reverseStream(final int[] array) {
return countdownFrom(array.length - 1).map(index -> array[index]);
}
public static DoubleStream reverseStream(final double[] array) {
return countdownFrom(array.length - 1).mapToDouble(index -> array[index]);
}
public static <T> Stream<T> reverseStream(final T[] array) {
return countdownFrom(array.length - 1).mapToObj(index -> array[index]);
}
public static IntStream countdownFrom(final int top) {
return IntStream.iterate(top, t -> t - 1).limit(max(0, (long) top + 1));
}
以下是一些证明其有效的测试:
import static java.lang.Integer.MAX_VALUE;
import static org.junit.Assert.*;
@Test
public void testReverseStream_emptyArrayCreatesEmptyStream() {
Assert.assertEquals(0, reverseStream(new double[0]).count());
}
@Test
public void testReverseStream_singleElementCreatesSingleElementStream() {
Assert.assertEquals(1, reverseStream(new double[1]).count());
final double[] singleElementArray = new double[] { 123.4 };
assertArrayEquals(singleElementArray, reverseStream(singleElementArray).toArray(), EXACT_MATCH);
}
@Test
public void testReverseStream_multipleElementsAreStreamedInReversedOrder() {
final double[] arr = new double[] { 1d, 2d, 3d };
final double[] revArr = new double[] { 3d, 2d, 1d };
Assert.assertEquals(arr.length, reverseStream(arr).count());
Assert.assertArrayEquals(revArr, reverseStream(arr).toArray(), EXACT_MATCH);
}
@Test
public void testCountdownFrom_returnsAllElementsFromTopToZeroInReverseOrder() {
assertArrayEquals(new int[] { 4, 3, 2, 1, 0 }, countdownFrom(4).toArray());
}
@Test
public void testCountdownFrom_countingDownStartingWithZeroOutputsTheNumberZero() {
assertArrayEquals(new int[] { 0 }, countdownFrom(0).toArray());
}
@Test
public void testCountdownFrom_doesNotChokeOnIntegerMaxValue() {
assertEquals(true, countdownFrom(MAX_VALUE).anyMatch(x -> x == MAX_VALUE));
}
@Test
public void testCountdownFrom_givesZeroLengthCountForNegativeValues() {
assertArrayEquals(new int[0], countdownFrom(-1).toArray());
assertArrayEquals(new int[0], countdownFrom(-4).toArray());
}
答案 21 :(得分:0)
在所有这些情况下,我看不到我会首先回答的问题。
这并不是对问题的直接答案,但这是对该问题的潜在解决方案。
首先将列表向后构建。如果可以,请使用LinkedList而不是ArrayList,添加项目时请使用“推”而不是add。该列表将以相反的顺序构建,然后将在不进行任何操作的情况下正确流式传输。
这不适用于您正在处理已经以各种方式使用但在许多情况下都能正常工作的原始数组或列表的情况。
答案 22 :(得分:0)
ArrayDeque
在堆栈中比Stack或LinkedList快。 “ push()”在Deque的前面插入元素
protected <T> Stream<T> reverse(Stream<T> stream) {
ArrayDeque<T> stack = new ArrayDeque<>();
stream.forEach(stack::push);
return stack.stream();
}
答案 23 :(得分:0)
反转字符串或任何数组
(Stream.of("abcdefghijklm 1234567".split("")).collect(Collectors.collectingAndThen(Collectors.toList(),list -> {Collections.reverse(list);return list;}))).stream().forEach(System.out::println);
split可以根据定界符或空格进行修改
答案 24 :(得分:0)
最简单的解决方案是使用List::listIterator
和Stream::generate
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
ListIterator<Integer> listIterator = list.listIterator(list.size());
Stream.generate(listIterator::previous)
.limit(list.size())
.forEach(System.out::println);
答案 25 :(得分:0)
列表newStream = list.stream()。sorted(Collections.reverseOrder())。collect(Collectors.toList()); newStream.forEach(System.out :: println);
答案 26 :(得分:0)
基于@stuart-marks's answer,但不进行强制转换,该函数从结尾开始返回列表元素流:
public static <T> Stream<T> reversedStream(List<T> tList) {
final int size = tList.size();
return IntStream.range(0, size)
.mapToObj(i -> tList.get(size - 1 - i));
}
// usage
reversedStream(list).forEach(System.out::println);
答案 27 :(得分:-1)
Java 8方法:
List<Integer> list = Arrays.asList(1,2,3,4);
Comparator<Integer> comparator = Integer::compare;
list.stream().sorted(comparator.reversed()).forEach(System.out::println);
答案 28 :(得分:-1)
反转列表的最通用和最简单的方法是:
public static <T> void reverseHelper(List<T> li){
li.stream()
.sorted((x,y)-> -1)
.collect(Collectors.toList())
.forEach(System.out::println);
}