Я уже читал этот и этот, но все же сомневаюсь, наблюдается ли наблюдаемое поведение Stream.skip
был предназначен авторами JDK.
Пусть имеет простой ввод чисел 1..20:
List<Integer> input = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
Теперь создайте параллельный поток, по-разному объедините unordered()
с skip()
и соберите результат:
System.out.println("skip-skip-unordered-toList: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.skip(1)
.unordered()
.collect(Collectors.toList()));
System.out.println("skip-unordered-skip-toList: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.unordered()
.skip(1)
.collect(Collectors.toList()));
System.out.println("unordered-skip-skip-toList: "
+ input.parallelStream().filter(x -> x > 0)
.unordered()
.skip(1)
.skip(1)
.collect(Collectors.toList()));
Шаг фильтрации здесь практически ничего не стоит, но добавляет больше проблем для движка потока: теперь он не знает точный размер вывода, поэтому некоторые оптимизации отключены. У меня есть следующие результаты:
skip-skip-unordered-toList: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
// absent values: 1, 2
skip-unordered-skip-toList: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20]
// absent values: 1, 15
unordered-skip-skip-toList: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20]
// absent values: 7, 18
Результаты полностью прекрасны, все работает так, как ожидалось. В первом случае я попросил пропустить первые два элемента, а затем собрать список без особого порядка. Во втором случае я попросил пропустить первый элемент, затем превратился в неупорядоченный и пропустить еще один элемент (мне все равно, какой). В третьем случае я сначала перешел в неупорядоченный режим, затем пропустил два произвольных элемента.
Позвольте пропустить один элемент и собрать в пользовательскую коллекцию в неупорядоченном режиме. Наша пользовательская коллекция будет HashSet
:
System.out.println("skip-toCollection: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.unordered()
.collect(Collectors.toCollection(HashSet::new)));
Выход удовлетворительный:
skip-toCollection: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
// 1 is skipped
В общем, я ожидаю, что до тех пор, пока поток упорядочен, skip()
пропускает первые элементы, в противном случае он пропускает произвольные.
Однако позвольте использовать эквивалентную неупорядоченную операцию терминала collect(Collectors.toSet())
:
System.out.println("skip-toSet: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.unordered()
.collect(Collectors.toSet()));
Теперь вывод:
skip-toSet: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20]
// 13 is skipped
Тот же результат может быть достигнут с любой другой неупорядоченной работой терминала (например, forEach
, findAny
, anyMatch
и т.д.). Удаление шага unordered()
в этом случае ничего не меняет. Кажется, что, хотя шаг unordered()
правильно делает поток неупорядоченным, начиная с текущей операции, неупорядоченная операция терминала делает весь поток неупорядоченным, начиная с самого начала, несмотря на то, что это может повлиять на результат, если использовался skip()
. Для меня это кажется совершенно неверным: я ожидаю, что использование неупорядоченного коллектора будет таким же, как превращение потока в неупорядоченный режим непосредственно перед операцией терминала и использование эквивалентного упорядоченного коллектора.
Итак, мои вопросы:
- Предполагается ли это поведение или это ошибка?
- Если да, то это где-то зарегистрировано? Я читал документацию Stream.skip(): он ничего не говорит о неупорядоченных терминальных операциях. Кроме того, Characteristics.UNORDERED документация не очень понятна и не говорит, что упорядочение будет потеряно для всего потока. Наконец, Ordering раздел в сводке пакетов также не распространяется на этот случай. Наверное, я что-то упустил?
- Если он предполагал, что неупорядоченная операция терминала делает весь поток неупорядоченным, почему шаг
unordered()
делает его неупорядоченным только с этой точки? Могу ли я полагаться на это поведение? Или мне просто повезло, что мои первые тесты работают хорошо?