javadoc для java.util.stream
подразумевает что «поведенческие операции» в конвейере потока обычно не должны иметь состояния. Однако показанные здесь примеры того, как не писать конвейер, похоже, включают параллельные потоки.
В какой степени это применимо к последовательным потокам?
В частности, я просматривал код коллеги, который выглядел примерно так:
List<SomeClass> list = ...;
Map<SomeClass, String> map = new HashMap<>();
list.stream()
.filter(x -> [some boolean expression])
.forEach(x -> {
if (map.containsKey(x) {
throw new UserDefinedException("duplicates detected in input");
} else {
map.put(x, aStringFunction(x));
}
});
[Автор пробовал использовать Collectors.toMap()
, но он выдавал IllegalStateException
, когда были дубликаты, и никто из нас не знал о toMap
, которое принимает mergeFunction
. Последнее было бы лучшим решением, но я все равно хотел бы получить ответ из-за более общего принципа.]
Я нервничал по поводу этого кода, так как мне было непонятно, может ли выполнение блока в forEach
перекрываться для разных элементов, даже для последовательного потока. javadoc для forEach()
немного неоднозначен, необходима ли синхронизация для доступа к общему состоянию в последовательном потоке. В конце концов автор изменил код, чтобы использовать ConcurrentHashMap
и map.putIfAbsent()
.
Мой вопрос: был ли я прав, что нервничал, или приведенный выше код заслуживает доверия?
Предположим, что выражение в filter()
сделало что-то, что использовало какое-то общее состояние. Можем ли мы верить, что он будет работать нормально при использовании последовательного потока?