Как проверить, отсортирован ли Stream <T>?

С помощью Iterable<T> легко:

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

Но я не могу придумать простой способ сделать то же самое для Stream<T>, который избегает потребления всех элементов, когда это не нужно.

Ответ 1

Существует несколько методов для итерации по последовательным парам потока. Например, вы можете проверить этот вопрос. Конечно, моим любимым методом является использование библиотеки, которую я написал:

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

Это короткое замыкание: оно будет закончено, как только оно обнаружит нарушение. Также он отлично работает с параллельными потоками.

Ответ 2

Вы можете захватить Stream spliterator и проверить, что он имеет SORTED-характеристику. Поскольку это операция с терминалом, вы не можете использовать Stream после (но вы можете создать еще один из этого разделителя, см. Также Преобразование Iterable в Stream с использованием 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 (в худшем случае - все элементы). Вы можете сделать задачу параллелизуемой с некоторым дополнительным кодом, а затем вам решать, стоит ли это или нет...

Ответ 3

Вы можете использовать allMatch с многострочной лямбдой, проверяя текущее значение по сравнению с предыдущим. Однако вам придется обернуть последнее значение в массив, поэтому лямбда может его изменить.

// 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

Вы можете захватить операцию сокращения, чтобы сохранить последнее значение и сравнить его с текущим значением и выдать исключение, если оно не отсортировано:

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

    return curr;
});

EDIT: я разыграл другой пример ответа и заменил его своим кодом, чтобы показать его, только необходимое количество проверок.

http://ideone.com/ZMGnVW

Ответ 5

Наивное решение использует Итератор потока:

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

Изменить: также можно было бы использовать spliterator для распараллеливания задачи, но выгоды были бы сомнительными, и увеличение сложности, вероятно, не стоит.

Ответ 6

Это последовательное, удерживающее состояние решение:

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 может быть рассмотрено в противном случае, но приведенное выше кажется самым простым.