Java 如何检查流是否<;T>;分类了吗?

Java 如何检查流是否<;T>;分类了吗?,java,java-8,java-stream,Java,Java 8,Java Stream,使用Iterable,您可以轻松地: T last = null; for (T t : iterable) { if (last != null && last.compareTo(t) > 0) { return false; } last = t; } return true; 但是我想不出一个干净的方法来为流做同样的事情,它可以避免在不需要的时候消耗所有元素。您可以获取流的底层拆分器并检查它是否具有排序特性。因为这是一个终端

使用
Iterable
,您可以轻松地:

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

但是我想不出一个干净的方法来为
流做同样的事情,它可以避免在不需要的时候消耗所有元素。

您可以获取流的底层拆分器并检查它是否具有排序特性。因为这是一个终端操作,所以不能在之后使用流(但是可以从这个拆分器创建另一个流,另请参见)

例如:

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);
Stream st=Stream.of(1,2,3);
//假的
布尔值isSorted=st.spliterator().hasCharacteristics(spliterator.SORTED);
Stream st=Stream.of(1,2,3).sorted();
//真的
布尔值isSorted=st.spliterator().hasCharacteristics(spliterator.SORTED);
我的示例显示,只有当您从报告
排序的
特征的源获取流,或者在管道上的某个点调用
排序()
时,
排序的
特征才会出现

有人可能认为
Stream.iterate(0,x->x+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; 
});

或者,只需从流中获取迭代器,并使用一个简单的循环。

您可以劫持一个缩减操作来保存最后一个值,并将其与当前值进行比较,如果未排序,则引发异常:

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

    return curr;
});
.stream().reduce((最后一个,当前)->{
如果((可比)货币)。与(上一次)相比<0{
抛出新异常();
}
返回货币;
});
编辑:我分叉了另一个答案的例子,并用我的代码替换它,以显示它只进行必要数量的检查


简单的解决方案使用流的迭代器:

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;
}
publicstaticbooleanissorted(流){
迭代器i=stream.Iterator();
如果(!i.hasNext())返回true;
T电流=i.next();
while(i.hasNext()){
T next=i.next();
如果(current==null | | current.compareTo(next)>0)返回false;
当前=下一个;
}
返回true;
}

编辑:也可以使用拆分器来并行化任务,但这样做的好处是值得怀疑的,复杂性的增加可能不值得。有几种方法可以迭代流的连续对。例如,您可以检查。当然,我最喜欢的方法是使用我写道:


这是短路操作:一旦发现故障就会结束。此外,它还适用于并行流。

这是一种顺序、状态保持的解决方案:

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
可能会以其他方式处理,但上面的问题似乎最简单。

我不知道它有多好,但我刚刚有了一个想法:

  • 从流、整数、字符串或任何内容中列出一个列表
  • 我为流的列表写了这篇文章:

  • “避免在不需要的情况下消耗所有元素”根据排序检查的定义,它不需要消耗所有元素吗?@AndyTurner对于
    [2,1,3,4,5,6,…]
    ,没有理由忽略前两个元素。哦,当然。但是,您必须查看流的末尾才能知道它已排序,例如,
    [1,2,3,4,5,……0]
    。不确定这个问题是否有意义:流可能是无限的,并且不能保证流能够被消耗两次。所以也许流不是你正在做的事情的一个选项?@GPI我不必使用它两次,而且我确信这个特定的流不是无限的。使用
    Stream
    API使实现非常漂亮和简洁,现在我只想测试它…正如您的示例所示,流可以有效地排序,而不具有排序特征。。。如果流具有排序特征,则对其进行排序,但如果流不具有排序特征,则可能对其进行排序,也可能不进行排序……这两个流都已排序。@assyl还需要迭代其他所有元素,但除非流是有限的,否则无法确定。如果你有一个无限长的管道,那么这是不可能的。那么唯一的方法就是迭代元素,直到你找到一个不遵守契约的元素,你就会停止,就像你已经做的那样。在你的问题中也要明确你对源代码的假设。而且,即使拆分器是
    排序的
    ,也不意味着它是按照自然顺序排序的。您至少应该检查
    getComparator()
    。a)在我看来,这甚至比(T T:(Iterable)stream::iterator){…
    b)的
    allMatch
    文档明确声明谓词必须是无状态的。现在尝试使用
    IntStream s=IntStream.of(1,2,3);
    s.parallel().allMatch(…)
    。@AlexisC.从来没有说过它可以与并行流一起工作(OP也从来没有要求过)。我知道这也是一个相当粗糙的解决方案。可能需要注意的是:只使用迭代器。@tobias_k我不特别关心顺序与并行,但我关心遵守流的API契约。但是,是的,它确实如此
    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));
    
            long countSorted = IntStream.range(1, listOfStream.size())
                    .map(
                            index -> {
                                if (listOfStream.get(index).compareTo(listOfStream.get(index-1)) > 0) {
                                    return 0;
                                }
                                return index;
                            })
                    .sum();