The 30 second answer
I group elements by identity, count them, and keep keys whose count is greater than one. That stays pure and works with parallel streams. The HashSet add trick is shorter but uses a stateful lambda, so I avoid it unless the stream is clearly sequential. Either way I mention equals and hashCode have to be correct.
Stateful shortcut (sequential only):
Set<T> seen = new HashSet<>();
Set<T> dups = list.stream()
.filter(x -> !seen.add(x))
.collect(Collectors.toSet());This uses a stateful lambda. The package summary warns that behavioral parameters should be stateless, and this breaks under parallel().
Cleaner pure version:
Set<T> dups = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toSet());Both are O(n) expected time with a good hashCode. They rely on correct equals / hashCode.
Sources:
Sources (0)
See inline citations in the answer above.