ord java_Java 8流反向ord

Java 8流反向ord

一般问题:反转流的正确方法是什么? 假设我们不知道流包含哪种类型的元素,反转任何流的通用方法是什么?

具体问题:

sorted()提供了在特定范围内生成整数的范围方法IntStream,现在我想要将其切换范围从0反转到负赢工作,我也不能使用Integer::compare

List list = Arrays.asList(1,2,3,4);

list.stream().sorted(Integer::compare).forEach(System.out::println);

与sorted()我将得到此编译器错误

错误:(191,0)ajc:类型IntStream中的方法sorted()不适用于参数(Integer::compare)

我在这里想念的是什么?

vach asked 2019-06-29T15:49:08Z

22个解决方案

56 votes

有关生成反向Deque的具体问题,请尝试以下操作:

static IntStream revRange(int from, int to) {

return IntStream.range(from, to)

.map(i -> to - i + from - 1);

}

这避免了装箱和分拣。

关于如何扭转任何类型的流的一般问题,我不知道那里有一个"正确的" 办法。 我有几种方法可以想到。 两者最终都存储了流元素。 我不知道如何在不存储元素的情况下反转流。

第一种方法将元素存储到一个数组中,并以相反的顺序将它们读出来。 请注意,由于我们不知道流元素的运行时类型,因此我们无法正确键入数组,因此需要未经检查的强制转换。

@SuppressWarnings("unchecked")

static Stream reverse(Stream input) {

Object[] temp = input.toArray();

return (Stream) IntStream.range(0, temp.length)

.mapToObj(i -> temp[temp.length - i - 1]);

}

另一种技术使用收集器将项目累积到反向列表中。 这会在Deque对象的前面进行大量插入,因此需要进行大量复制。

Stream input = ... ;

List output =

input.collect(ArrayList::new,

(list, e) -> list.add(0, e),

(list1, list2) -> list1.addAll(0, list2));

使用某种定制数据结构编写更高效的反转收集器可能是可能的。

更新2016-01-29

由于这个问题最近引起了一些关注,我想我应该更新我的答案来解决问题,插入前面的Deque.这将是非常低效的大量元素,需要O(N ^ 2)复制。

相反,最好使用Deque,它可以有效地支持前面的插入。 一个小皱纹是我们不能使用244000669230367745的三个arg形式; 它要求第二个arg的内容合并到第一个arg中,并且没有" add-all-at-front" 批量操作Deque.相反,我们使用addAll()将第一个arg的内容追加到第二个arg的末尾,然后我们返回第二个arg。 这需要使用Collector.of()工厂方法。

完整的代码是这样的:

Deque output =

input.collect(Collector.of(

ArrayDeque::new,

(deq, t) -> deq.addFirst(t),

(d1, d2) -> { d2.addAll(d1); return d2; }));

结果是Deque而不是List,但这不应该成为一个问题,因为它可以很容易地以现在颠倒的顺序进行迭代或流式传输。

Stuart Marks answered 2019-06-29T15:51:16Z

33 votes

优雅的解决方

List list = Arrays.asList(1,2,3,4);

list.stream()

.boxed() // Converts Intstream to Stream

.sorted(Collections.reverseOrder()) // Method on Stream

.forEach(System.out::println);

Kishan B answered 2019-06-29T15:51:48Z

30 votes

这里的许多解决方案对IntStream进行排序或反转,但这不必要地需要中间存储。 Stuart Marks的解决方案是要走的路:

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});

}

Brandon Mintern answered 2019-06-29T15:52:26Z

28 votes

一般问题:

Stream不存储任何元素。

因此,如果不将元素存储在某个中间集合中,则无法以相反的顺序迭代元素。

Stream.of("1", "2", "20", "3")

.collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList

.descendingIterator()

.forEachRemaining(System.out::println);

更新:将LinkedList更改为ArrayDeque(更好)请参阅此处了解详细信息

打印:

3

20

2

1

顺便说一句,使用sort方法是不正确的,因为它排序,不反转(假设流可能有无序元素)

具体问题:

我发现这个简单,容易和直观(复制@Holger评论)

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)

Venkata Raju answered 2019-06-29T15:53:55Z

14 votes

没有外部lib ...

import java.util.List;

import java.util.Collections;

import java.util.stream.Collector;

public class MyCollectors {

public static Collector> toListReversed() {

return Collectors.collectingAndThen(Collectors.toList(), l -> {

Collections.reverse(l);

return l;

});

}

}

comonad answered 2019-06-29T15:54:27Z

13 votes

如果实施Comparable(例如,Integer,String,Date),则可以使用Comparator.reverseOrder()执行此操作。

List list = Arrays.asList(1, 2, 3, 4);

list.stream()

.sorted(Comparator.reverseOrder())

.forEach(System.out::println);

YujiSoftware answered 2019-06-29T15:54:59Z

10 votes

您可以定义自己的收集器,以相反的顺序收集元素:

public static Collector, List> inReverse() {

return Collector.of(

ArrayList::new,

(l, t) -> l.add(t),

(l, r) -> {l.addAll(r); return l;},

Lists::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, List> inReverse = inReverse();

private final Supplier> supplier = inReverse.supplier();

private final BiConsumer, Object> accumulator = inReverse.accumulator();

private final Function, List> finisher = inReverse.finisher();

private final BinaryOperator> combiner = inReverse.combiner();

@Test public void associative() {

final List a1 = supplier.get();

accumulator.accept(a1, t1);

accumulator.accept(a1, t2);

final List r1 = finisher.apply(a1);

final List a2 = supplier.get();

accumulator.accept(a2, t1);

final List a3 = supplier.get();

accumulator.accept(a3, t2);

final List r2 = finisher.apply(combiner.apply(a2, a3));

assertThat(r1, Matchers.equalTo(r2));

}

@Test public void identity() {

final List a1 = supplier.get();

accumulator.accept(a1, t1);

accumulator.accept(a1, t2);

final List r1 = finisher.apply(a1);

final List a2 = supplier.get();

accumulator.accept(a2, t1);

accumulator.accept(a2, t2);

final List r2 = finisher.apply(combiner.apply(a2, supplier.get()));

assertThat(r1, equalTo(r2));

}

@Test public void reversing() throws Exception {

final List a2 = supplier.get();

accumulator.accept(a2, t1);

accumulator.accept(a2, t2);

final List a3 = supplier.get();

accumulator.accept(a3, t3);

accumulator.accept(a3, t4);

final List r2 = finisher.apply(combiner.apply(a2, a3));

assertThat(r2, contains(t4, t3, t2, t1));

}

public static Collector, List> inReverse() {

return Collector.of(

ArrayList::new,

(l, t) -> l.add(t),

(l, r) -> {l.addAll(r); return l;},

Lists::reverse);

}

}

lexicalscope answered 2019-06-29T15:55:57Z

9 votes

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);

John McClean answered 2019-06-29T15:56:46Z

6 votes

这是我提出的解决方案:

private static final Comparator BY_ASCENDING_ORDER = Integer::compare;

private static final Comparator BY_DESCENDING_ORDER = BY_ASCENDING_ORDER.reversed();

然后使用那些比较器:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...

vach answered 2019-06-29T15:57:29Z

4 votes

我建议使用jOOλ,它是一个很棒的库,为Java 8流和lambdas添加了许多有用的功能。

然后,您可以执行以下操作:

List list = Arrays.asList(1,2,3,4);

Seq.seq(list).reverse().forEach(System.out::println)

就那么简单。 它是一个非常轻量级的库,值得添加到任何Java 8项目中。

lukens answered 2019-06-29T15:58:18Z

3 votes

最简单的方法(简单收集 - 支持并行流):

public static Stream reverse(Stream stream) {

return stream

.collect(Collector.of(

() -> new ArrayDeque(),

ArrayDeque::addFirst,

(q1, q2) -> { q2.addAll(q1); return q2; })

)

.stream();

}

高级方式(以持续方式支持并行流):

public static Stream reverse(Stream stream) {

Objects.requireNonNull(stream, "stream");

class ReverseSpliterator implements Spliterator {

private Spliterator spliterator;

private final Deque deque = new ArrayDeque<>();

private ReverseSpliterator(Spliterator 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 trySplit() {

// After traveling started the spliterator don't contain elements!

Spliterator prev = spliterator.trySplit();

if(prev == null) {

return null;

}

Spliterator 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与其他流操作一起使用时不是很有用(收集连接会破坏并行性)。 advance way没有这个问题,并且它还保留了流的初始特征,例如SORTED,因此,它是在反向之后与其他流操作一起使用的方式。

Tet answered 2019-06-29T15:59:23Z

2 votes

人们可以编写一个收集器,以相反的顺序收集元素:

public static Collector> 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 Stream reverse(Stream stream) {

LinkedList stack = new LinkedList<>();

stream.forEach(stack::push);

return stack.stream();

}

SlavaSt answered 2019-06-29T16:00:19Z

2 votes

这种实用方法怎么样?

public static Stream getReverseStream(List list) {

final ListIterator listIt = list.listIterator(list.size());

final Iterator reverseIterator = new Iterator() {

@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);

}

似乎可以处理所有情况而无需重复。

Jerome answered 2019-06-29T16:00:59Z

1 votes

作为参考,我正在查看相同的问题,我想以相反的顺序加入流元素的字符串值。

itemList = {last,middle,first} =&gt;第一,中间,最后

我开始使用来自comonad的insert或Stuart Marks的ArrayDeque收集器的中间集合,尽管我对中间集合和再次流式传输不满意

itemList.stream()

.map(TheObject::toString)

.collect(Collectors.collectingAndThen(Collectors.toList(),

strings -> {

Collections.reverse(strings);

return strings;

}))

.stream()

.collect(Collector.joining());

所以我重复使用了insert工厂的Stuart Marks答案,该工厂有一个有趣的终结者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方法。

Brice answered 2019-06-29T16:02:12Z

1 votes

通过IntStream回答具体的倒车问题,下面对我有用:

IntStream.range(0, 10)

.map(x -> x * -1)

.sorted()

.map(Math::abs)

.forEach(System.out::println);

Girish Jain answered 2019-06-29T16:02:43Z

1 votes

关于生成反转的具体问题next - Java 9引入了另一种方法next,可以很容易地用于以相反的顺序迭代:

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

方法描述:

next

next - 初始元素;

next - 一个谓词,用于应用于元素以确定何时流必须终止;

next - 要应用于前一个元素以生成a的函数新元素。

Oleksandr answered 2019-06-29T16:03:54Z

1 votes

不仅仅是Java 8,但如果你结合使用guava的Lists.reverse()方法,你可以轻松实现这个目的:

List list = Arrays.asList(1,2,3,4);

Lists.reverse(list).stream().forEach(System.out::println);

Jeffrey answered 2019-06-29T16:04:29Z

0 votes

我就是这样做的。

我不喜欢创建新集合并反向迭代它的想法。

IntStream#map的想法非常简洁,但我更喜欢IntStream#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 Stream 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());

}

Brixomatic answered 2019-06-29T16:05:21Z

0 votes

在这一切中,我没有看到我会先回答的答案。

这不是问题的直接答案,但它是解决问题的潜在方法。

只需首先构建列表。 如果可以,请使用LinkedList而不是ArrayList,当您添加项目时,使用&#34; Push&#34; 而不是添加。 该列表将以相反的顺序构建,然后无需任何操作即可正确流式传输。

这是一个不合适的案例,您处理的原始数组或列表已经以各种方式使用,但在令人惊讶的情况下运行良好。

Bill K answered 2019-06-29T16:06:23Z

0 votes

ArrayDeque堆栈中的速度比Stack或LinkedList快。&#34;推()&#34; 在Deque的前面插入元素

protected Stream reverse(Stream stream) {

ArrayDeque stack = new ArrayDeque<>();

stream.forEach(stack::push);

return stack.stream();

}

dotista2008 answered 2019-06-29T16:06:59Z

-1 votes

反向列表的最通用和最简单的方法是:

public static void reverseHelper(List li){

li.stream()

.sorted((x,y)-> -1)

.collect(Collectors.toList())

.forEach(System.out::println);

}

parmeshwor11 answered 2019-06-29T16:07:42Z

-1 votes

Java 8的方法:

List list = Arrays.asList(1,2,3,4);

Comparator comparator = Integer::compare;

list.stream().sorted(comparator.reversed()).forEach(System.out::println);

Antonio answered 2019-06-29T16:08:22Z

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值