I have this commands:
list.stream()
.filter(e -> ...)
.sorted(comparatorShuffle())
.findAny()
.orElse(null);
This is comparatorShuffle()
:
public static <E> Comparator<E> comparatorShuffle(){
return new Comparator<>(){
private final List<Integer> temp = List.of(-1, 1);
private final Random random = new Random();
@Override
@SuppressWarnings("ComparatorMethodParameterNotUsed")
public int compare(E o1, E o2){
return temp.get(random.nextInt(temp.size()));
}
};
}
Sometimes I get an exception: IllegalArgumentException: Comparison method violates its general contract!
I understand why I get this, it's because the sort(it's random) don't respect the rule: if A > B && B > C then A > C
There is a way to suppress/ignore this error? Or another way to shuffle the stream without using collect
?
CodePudding user response:
There is a way to suppress/ignore this error?
No.
You're not really shuffling here, you're ostensibly just trying to pick a random element from the stream.
To do this, you can pair up the elements with a random number and pick the min/max:
...
.map(a -> new AbstractMap.SimpleEntry<>(Math.random(), a))
.min(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.orElse(null)
Alternatively, you can write a custom collector which randomly picks between two things when merging:
.collect(
Collector.of(
// Use Optional.empty() as the identity element, if the stream is empty.
Optional::empty,
// Optionalize the element, taking it randomly or if we have the identity element.
(a, b) -> a.isEmpty() || Math.random() > 0.5 ? Optional.of(b) : a,
// Merge.
(a, b) -> Math.random() > 0.5 ? b : a,
// Unwrap the value.
r -> r.orElse(null)))
The big problem with this alternative is it doesn't pick uniformly from the stream (you're not equally likely to get any one of the elements from the stream), whereas you are with the first way.