vac*_*ach 135 java java-8 java-stream
一般问题:反转流的正确方法是什么?假设我们不知道流包含哪种类型的元素,那么反转任何流的通用方法是什么?
具体问题:
IntStream
提供范围方法来生成特定范围内的整数IntStream.range(-range, 0)
,现在我要反转它切换范围从0到负不起作用,我也不能使用Integer::compare
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
与IntStream
我会得到这个编译器错误
错误:(191,0)ajc:
sorted()
类型中的方法IntStream
不适用于参数(Integer::compare
)
我在这里想念的是什么?
Stu*_*rks 67
对于生成反向的具体问题IntStream
,请尝试以下方法:
static IntStream revRange(int from, int to) {
return IntStream.range(from, to)
.map(i -> to - i + from - 1);
}
Run Code Online (Sandbox Code Playgroud)
这避免了装箱和分拣.
对于如何反转任何类型的流的一般问题,我不知道有一种"正确"的方式.我有几种方法可以想到.两者最终都存储了流元素.我不知道如何在不存储元素的情况下反转流.
第一种方法将元素存储到一个数组中,并以相反的顺序将它们读出来.请注意,由于我们不知道流元素的运行时类型,因此我们无法正确键入数组,需要未经检查的强制转换.
@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]);
}
Run Code Online (Sandbox Code Playgroud)
另一种技术使用收集器将项目累积到反向列表中.这会在ArrayList
对象的前面进行大量插入,因此会进行大量复制.
Stream<T> input = ... ;
List<T> output =
input.collect(ArrayList::new,
(list, e) -> list.add(0, e),
(list1, list2) -> list1.addAll(0, list2));
Run Code Online (Sandbox Code Playgroud)
使用某种自定义数据结构编写更高效的可逆回收器可能是可能的.
更新2016-01-29
由于这个问题最近引起了一些关注,我想我应该更新我的答案来解决问题,插入前面ArrayList
.对于大量元素,这将是非常低效的,需要O(N ^ 2)复制.
最好使用ArrayDeque
替代,它有效地支持前面的插入.一个小皱纹是我们不能使用三个arg形式Stream.collect()
; 它要求将第二个arg的内容合并到第一个arg中,并且没有"add-all-at-front"批量操作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; }));
Run Code Online (Sandbox Code Playgroud)
结果是a Deque
而不是a List
,但这不应该是一个很大的问题,因为它可以很容易地以现在颠倒的顺序进行迭代或流式传输.
Kis*_*n B 35
优雅的解决方
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream()
.boxed() // Converts Intstream to Stream<Integer>
.sorted(Collections.reverseOrder()) // Method on Stream<Integer>
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
Rea*_*ted 33
这里的许多解决方案排序或反转IntStream
,但这不必要地需要中间存储.Stuart Marks的解决方案是要走的路:
static IntStream revRange(int from, int to) {
return IntStream.range(from, to).map(i -> to - i + from - 1);
}
Run Code Online (Sandbox Code Playgroud)
它正确处理溢出,通过此测试:
@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});
}
Run Code Online (Sandbox Code Playgroud)
Ven*_*aju 30
一般问题:
Stream不存储任何元素.
因此,如果不将元素存储在某个中间集合中,则无法以相反的顺序迭代元素.
Stream.of("1", "2", "20", "3")
.collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList
.descendingIterator()
.forEachRemaining(System.out::println);
Run Code Online (Sandbox Code Playgroud)
更新:将LinkedList更改为ArrayDeque(更好)请参阅此处了解详细信息
打印:
3
20
2
1
Run Code Online (Sandbox Code Playgroud)
顺便说一下,使用sort
方法不正确,因为它排序,而不是反转(假设流可能有无序元素)
具体问题:
我发现这个简单,容易和直观(复制@Holger评论)
IntStream.iterate(to - 1, i -> i - 1).limit(to - from)
Run Code Online (Sandbox Code Playgroud)
com*_*nad 15
没有外部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;
});
}
}
Run Code Online (Sandbox Code Playgroud)
Yuj*_*are 15
如果实施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);
Run Code Online (Sandbox Code Playgroud)
luk*_*uke 11
如何不这样做:
.sorted(Comparator.reverseOrder())
or .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);
Run Code Online (Sandbox Code Playgroud)
输出:
[3, 5, 2, 4, 1]
相同的String
:
List<String> stringList = Arrays.asList("A", "D", "B", "E", "C");
Collections.reverse(stringList);
System.out.println(stringList);
Run Code Online (Sandbox Code Playgroud)
输出:
[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);
Run Code Online (Sandbox Code Playgroud)
正例输出:
[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);
Run Code Online (Sandbox Code Playgroud)
正例输出:
[C, E, B, D, A]
并行流或其他 JVM 实现中的可能输出:
[A, E, B, D, C]
lex*_*ope 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);
}
Run Code Online (Sandbox Code Playgroud)
并使用它像:
stream.collect(inReverse()).forEach(t -> ...)
Run Code Online (Sandbox Code Playgroud)
我以正向顺序使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
cyclops -react StreamUtils有一个反向流方法(javadoc).
StreamUtils.reverse(Stream.of("1", "2", "20", "3"))
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
它的工作原理是收集到一个ArrayList,然后使用可以向任一方向迭代的ListIterator类,在列表上向后迭代.
如果您已经有一个List,它将更有效率
StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
.forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)
这是我提出的解决方案:
private static final Comparator<Integer> BY_ASCENDING_ORDER = Integer::compare;
private static final Comparator<Integer> BY_DESCENDING_ORDER = BY_ASCENDING_ORDER.reversed();
Run Code Online (Sandbox Code Playgroud)
然后使用那些比较器:
IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...
Run Code Online (Sandbox Code Playgroud)
我建议使用jOO?,它是一个很棒的库,为Java 8流和lambda添加了许多有用的功能。
然后,您可以执行以下操作:
List<Integer> list = Arrays.asList(1,2,3,4);
Seq.seq(list).reverse().forEach(System.out::println)
Run Code Online (Sandbox Code Playgroud)
就那么简单。这是一个非常轻量级的库,非常值得添加到任何Java 8项目中。
这个实用方法怎么样?
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);
}
Run Code Online (Sandbox Code Playgroud)
似乎适用于所有情况而不会重复。
关于生成反向的具体问题IntStream
:
从Java 9开始,您可以使用三参数版本IntStream.iterate(...)
:
IntStream.iterate(10, x -> x >= 0, x -> x - 1).forEach(System.out::println);\n\n// Out: 10 9 8 7 6 5 4 3 2 1 0\n
Run Code Online (Sandbox Code Playgroud)\n\n在哪里:
\n\nIntStream.iterate\xe2\x80\x8b(int seed, IntPredicate hasNext, IntUnaryOperator next);
seed
- 初始元素;hasNext
- 应用于元素的谓词以确定\n流必须终止的时间;next
- 应用于前一个元素以生成\n新元素的函数。 归档时间: |
|
查看次数: |
141513 次 |
最近记录: |