我该如何检查Stream <t>是否已排序?

时间:2015-05-28 14:14:01

标签: java java-8 java-stream

使用Iterable<T>,很容易:

T last = null;
for (T t : iterable) {
    if (last != null && last.compareTo(t) > 0) {
        return false;
    }
    last = t;
}
return true;

但我想不出一个干净的方法来为Stream<T>做同样的事情,避免在不需要时消耗所有元素。

7 个答案:

答案 0 :(得分:8)

有几种方法可以迭代连续的流对。例如,您可以查看this question。当然我最喜欢的方法是使用the library我写道:

boolean unsorted = StreamEx.of(sourceStream)
                           .pairMap((a, b) -> a.compareTo(b) > 0)
                           .has(true);

这是短路操作:一旦发现错误订单就会完成。它也适用于并行流。

答案 1 :(得分:3)

你可以抓住Stream的底层分裂器并检查它是否具有SORTED特性。由于它是终端操作,因此您无法使用Stream(但您可以从此分词器中创建另一个,请参阅Convert Iterable to Stream using Java 8 JDK)。

例如:

Stream<Integer> st = Stream.of(1, 2, 3);
//false
boolean isSorted = st.spliterator().hasCharacteristics(Spliterator.SORTED);

Stream<Integer> st = Stream.of(1, 2, 3).sorted();
//true
boolean isSorted = st.spliterator().hasCharacteristics(Spliterator.SORTED);

我的示例显示SORTED特征仅在您从报告SORTED特征的来源获得流或在您的某个点上调用sorted()时才会显示。管道。

有人可能会争辩Stream.iterate(0, x -> x + 1);创建一个SORTED流,但是不知道迭代应用的函数的语义。这同样适用于Stream.of(...)

如果管道是无限的,那么它是唯一知道的方法。如果没有,并且分裂者没有报告此特征,您需要浏览元素并查看它是否不满足您正在寻找的排序特征。

这是你已经用迭代器方法完成的,但是你需要消耗Stream的一些元素(在最坏的情况下,所有元素)。您可以使用一些额外的代码使任务可并行化,然后由您决定它是否值得...

答案 2 :(得分:3)

您可以劫持减少操作以保存最后一个值并将其与当前值进行比较,如果没有排序则抛出异常:

.stream().reduce((last, curr) -> {
   if (((Comparable)curr).compareTo(last) < 0) {
       throw new Exception();
    }

    return curr;
});

编辑:我分叉了另一个答案的例子,并用我的代码替换它,表明它只进行了必要的检查次数。

http://ideone.com/ZMGnVW

答案 3 :(得分:1)

您可以将allMatch与多行lambda一起使用,检查当前值与前一个值。但是,您必须将最后一个值包装到数组中,因此lambda可以修改它。

// infinite stream with one pair of unsorted numbers
IntStream s = IntStream.iterate(0, x -> x != 1000 ? x + 2 : x - 1);
// terminates as soon as the first unsorted pair is found
int[] last = {Integer.MIN_VALUE};
boolean sorted = s.allMatch(x -> {
    boolean b = x >= last[0]; last[0] = x; return b; 
});

或者,只需从流中获取iterator并使用简单的循环。

答案 4 :(得分:1)

天真的解决方案使用流的迭代器:

public static <T extends Comparable<T>> boolean isSorted(Stream<T> stream) {
    Iterator<T> i = stream.iterator();
    if(!i.hasNext()) return true;
    T current = i.next();
    while(i.hasNext()) {
        T next = i.next();
        if(current == null || current.compareTo(next) > 0) return false;
        current = next;
    }
    return true;
}

编辑:也可以使用分裂器来并行化任务,但增益会有问题,复杂性的增加可能不值得。

答案 5 :(得分:1)

这是一个顺序的州持股解决方案:

IntStream stream = IntStream.of(3, 3, 5, 6, 6, 9, 10);
final AtomicInteger max = new AtomicInteger(Integer.MIN_VALUE);
boolean sorted = stream.allMatch(n -> n >= max.getAndSet(n));

并行化需要引入范围。状态max可能会以其他方式处理,但上述情况似乎最简单。

答案 6 :(得分:0)

我不知道它有多好,但是我有一个主意:

  1. 从Stream,Integer或Strings或其他内容中列出一个列表。
  2. 我为List<String> listOfStream撰写了这篇文章:
        long countSorted = IntStream.range(1, listOfStream.size())
                .map(
                        index -> {
                            if (listOfStream.get(index).compareTo(listOfStream.get(index-1)) > 0) {
                                return 0;
                            }
                            return index;
                        })
                .sum();