【问题标题】:Shuffle Stream using sort - IllegalArgumentException使用排序随机播放流 - IllegalArgumentException
【发布时间】:2021-10-01 11:32:58
【问题描述】:

我有这个命令:

list.stream()
    .filter(e -> ...)
    .sorted(comparatorShuffle())
    .findAny()
    .orElse(null);

这是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()));
        }
    };

}

有时我会遇到异常:IllegalArgumentException: Comparison method violates its general contract!

我明白我为什么会得到这个,这是因为排序(它是随机的)不遵守规则:if A &gt; B &amp;&amp; B &gt; C then A &gt; C

有办法抑制/忽略此错误吗?或者不使用collect 的另一种方式来随机播放流?

【问题讨论】:

  • 您的比较器不是合适的比较器。它违反了比较器协议。首先,这也不是一种随机采样或随机播放的明智方式。
  • @kaya3 “它违反了比较器协议” OP 知道这一点(“我明白我为什么得到这个”)。

标签: java java-stream


【解决方案1】:

有办法抑制/忽略此错误吗?

没有。

你并没有真正在这里洗牌,你表面上只是想从流中挑选一个随机元素。

为此,您可以将元素与随机数配对并选择最小值/最大值:

...
.map(a -> new AbstractMap.SimpleEntry<>(Math.random(), a))
.min(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.orElse(null)

或者,您可以编写一个自定义收集器,在合并时在两个事物之间随机选择:

.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)))

这种替代方案的大问题是它不能从流中统一选择(您不太可能从流中获取任何元素),而您使用第一种方式。

【讨论】:

  • You're not really shuffling here, you're ostensibly just trying to pick a random element from the stream.true
  • 我在Optional::of - Static method referenced through receiver 收到警告
  • new AbstractMap.SimpleEntry&lt;&gt;(k, v) ...或只是Map.entry(k, v)
  • 您实际上可以使用reservoir sampling 解决非均匀性问题。最简单的版本是:您的代码使用Math.random() &gt; 0.5,而是将阈值设为1 / [num items seen so far]
  • 如果您想要一个正确的Collector 选择一个随机元素,请考虑this answer。如果您想按实际改组的顺序对所有元素进行排序,请参阅Shuffle/Random comparator
猜你喜欢
  • 2014-07-26
  • 2023-01-23
  • 2023-03-23
  • 1970-01-01
  • 2019-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多