Java 随机化列表处理速度比集合快。shuffle()?

Java 随机化列表处理速度比集合快。shuffle()?,java,performance,random,collections,shuffle,Java,Performance,Random,Collections,Shuffle,我正在用Java开发一个基于代理的模型。我使用了一个探查器来降低任何低效率,因为唯一阻碍它的是Java的Collections.shuffle 在我的模型中,作为动物的代理需要以随机顺序进行处理,这样就不会有代理在其他代理之前得到一致的处理 我正在寻找:要么是一种比Java的Collections.shuffle更快的洗牌方法,要么是一种以随机顺序处理ArrayList中元素的替代方法,速度要快得多。如果您知道一种比ArrayList更快的数据结构,请务必回答。我曾考虑过LinkedList和A

我正在用Java开发一个基于代理的模型。我使用了一个探查器来降低任何低效率,因为唯一阻碍它的是Java的Collections.shuffle

在我的模型中,作为动物的代理需要以随机顺序进行处理,这样就不会有代理在其他代理之前得到一致的处理

我正在寻找:要么是一种比Java的Collections.shuffle更快的洗牌方法,要么是一种以随机顺序处理ArrayList中元素的替代方法,速度要快得多。如果您知道一种比ArrayList更快的数据结构,请务必回答。我曾考虑过LinkedList和ArrayDeque,但它们没有多大区别

目前,我正在尝试洗牌的列表中有超过1000000个元素。随着时间的推移,这一数量不断增加,洗牌的效率越来越低

是否有另一种数据结构或随机化元素处理的方法更快

我只需要能够存储元素并以随机顺序处理它们。我不使用contains或任何比存储和迭代更复杂的东西

以下是一些示例代码,可以更好地解释我试图实现的目标:

更新:很抱歉ConcurrentModificationException,我不知道我已经这么做了,我也不想让任何人感到困惑。在下面的代码中修复了它

ArrayList<Agent> list = new ArrayList<>();
void process()
{
    list.add(new Agent("Zebra"));
    Random r = new Random();
    for (int i = 0; i < 100000; i++)
    {
        ArrayList<Agent> newlist = new ArrayList<>();
        Collections.shuffle(list);//Something that will allow the order to be random (random quality does not matter to me), yet faster than a shuffle
        for (String str : list)
        {
            newlist.add(str);
            if(r.nextDouble() > 0.99)//1% chance of adding another agent to the list
            {
                newlist.add(new Agent("Lion"));
            }
        }
        list = newlist;
    }
}
另一个更新
我曾考虑过使用list.removerando.nextInList.size,但由于启用了针对ArrayList的删除功能,因此使用该功能比对如此大的列表大小进行洗牌更糟糕。

您可以使用此功能:如果您已经拥有项目列表,则根据其大小生成一个随机数并获取nextInt

ArrayList<String> list = new ArrayList<>();    
int sizeOfCollection = list.size();

Random randomGenerator = new Random();
int randomId = randomGenerator.nextInt(sizeOfCollection);
Object x = list.get(randomId);
list.remove(randomId);

您可以这样做:如果您已经有了项目列表,则根据其大小生成一个随机列表,并获取nextInt

ArrayList<String> list = new ArrayList<>();    
int sizeOfCollection = list.size();

Random randomGenerator = new Random();
int randomId = randomGenerator.nextInt(sizeOfCollection);
Object x = list.get(randomId);
list.remove(randomId);

由于您的代码实际上并不依赖于列表的顺序,因此在处理结束时对其进行一次洗牌就足够了

void process() {
    Random r = new Random();
    for (int i = 0; i < 100000; i++) {
        for (String str : list) {
             if(r.nextDouble() > 0.9) {
                list.add(str + str);
            }
        }
    }
    Collections.shuffle(list);
}

尽管这仍然会抛出ConcurrentModificationException,就像原始代码一样。

因为您的代码实际上并不依赖于列表的顺序,所以在处理结束时将其洗牌一次就足够了

void process() {
    Random r = new Random();
    for (int i = 0; i < 100000; i++) {
        for (String str : list) {
             if(r.nextDouble() > 0.9) {
                list.add(str + str);
            }
        }
    }
    Collections.shuffle(list);
}

尽管这仍然会引发ConcurrentModificationException,就像原始代码一样。

Collections.shuffle使用Fisher-Yates算法的现代变体: 从

Colections.shuffle将列表转换为一个数组,然后使用random.nextInt进行洗牌,然后将所有内容复制回来。看

通过避免复制数组和写回的开销,您可以加快速度: 可以编写自己的ArrayList实现,在其中可以直接访问支持数组,也可以通过反射访问ArrayList的field elementData

现在使用与Collections.shuffle相同的算法,在该数组上使用正确的大小。 这会加快速度,因为如果整个数组(如Collection.shuffle)执行以下操作,则可以避免复制:

通过反射进行访问需要一点时间,因此此解决方案仅适用于更多的元素

我不会推荐这个解决方案,除非你想赢得这场比赛,有快速洗牌,也通过执行时间


和往常一样,在比较速度时,请确保在开始测量之前运行1000次要测量的算法来预热VM。

Collections.shuffle使用Fisher-Yates算法的现代变体: 从

Colections.shuffle将列表转换为一个数组,然后使用random.nextInt进行洗牌,然后将所有内容复制回来。看

通过避免复制数组和写回的开销,您可以加快速度: 可以编写自己的ArrayList实现,在其中可以直接访问支持数组,也可以通过反射访问ArrayList的field elementData

现在使用与Collections.shuffle相同的算法,在该数组上使用正确的大小。 这会加快速度,因为如果整个数组(如Collection.shuffle)执行以下操作,则可以避免复制:

通过反射进行访问需要一点时间,因此此解决方案仅适用于更多的元素

我不会推荐这个解决方案,除非你想赢得这场比赛,有快速洗牌,也通过执行时间


和往常一样,在比较速度时,确保在开始测量之前,通过运行1000次要测量的算法来预热VM。

我将使用一个简单的ArrayList,而不是将其洗牌。而是选择要处理的随机列表索引。为了避免对列表元素进行两次处理,我将从列表中删除已处理的元素

现在,如果列表非常大,那么删除随机条目本身就是瓶颈。但是,可以通过删除最后一个条目并将其移动到所选条目占用的位置来轻松避免此问题 矿石:


不用说,您应该选择一个列表实现,其中getindex和removelastinex是fastO1,比如ArrayList。您可能还需要添加边缘大小写处理,例如list is empty。

我将使用一个简单的ArrayList,而不是将其洗牌。而是选择要处理的随机列表索引。为了避免对列表元素进行两次处理,我将从列表中删除已处理的元素

现在,如果列表非常大,那么删除随机条目本身就是瓶颈。但是,可以通过移除最后一个条目,并将其移动到选定条目之前占用的位置来轻松避免:

public String pullRandomElement(List<String> list, Random random) {
    // select a random list index
    int size = list.size();
    int index = random.nextInt(size);
    String result = list.get(index);
    // move last entry to selected index
    list.set(index, list.remove(size - 1));
    return result;
}

不用说,您应该选择一个列表实现,其中getindex和removelastinex是fastO1,比如ArrayList。您可能还需要添加边缘案例处理,例如列表为空。

根据文档,Collections.shuffle会按时运行

此方法以线性时间运行。如果指定的列表未实现RandomAccess接口且较大,则此实现会在洗牌之前将指定列表转储到数组中,并将洗牌后的数组转储回列表中。这避免了将顺序访问列表洗牌时产生的二次行为

我建议您使用publicstaticvoidshufflelist列表,即随机rnd重载,尽管其性能优势可能微不足道

除非允许一些偏差,例如使用部分洗牌,每次或在洗牌下仅重新洗牌列表的一部分,否则很难提高性能。洗牌意味着编写自己的Fisher-Yates例程,并在反向遍历过程中跳过某些列表索引;例如,您可以跳过所有奇数索引。然而,你的列表的末尾会比前面少一些混乱,这是另一种形式的偏见


如果你有一个固定的列表大小m,你可能会考虑在应用程序启动时在内存中随机地将大量n个不同的固定索引排列0随机地存储在M-1中。然后,您可以在迭代集合时随机选择这些预排序中的一个,并根据之前定义的特定排列进行迭代。如果N很大,比如说1000或更多,总体偏差会很小,也相对均匀,并且会非常快。然而,您注意到列表增长缓慢,因此这种方法不可行。

根据文档,Collections.shuffle会按时运行

此方法以线性时间运行。如果指定的列表未实现RandomAccess接口且较大,则此实现会在洗牌之前将指定列表转储到数组中,并将洗牌后的数组转储回列表中。这避免了将顺序访问列表洗牌时产生的二次行为

我建议您使用publicstaticvoidshufflelist列表,即随机rnd重载,尽管其性能优势可能微不足道

除非允许一些偏差,例如使用部分洗牌,每次或在洗牌下仅重新洗牌列表的一部分,否则很难提高性能。洗牌意味着编写自己的Fisher-Yates例程,并在反向遍历过程中跳过某些列表索引;例如,您可以跳过所有奇数索引。然而,你的列表的末尾会比前面少一些混乱,这是另一种形式的偏见


如果你有一个固定的列表大小m,你可能会考虑在应用程序启动时在内存中随机地将大量n个不同的固定索引排列0随机地存储在M-1中。然后,您可以在迭代集合时随机选择这些预排序中的一个,并根据之前定义的特定排列进行迭代。如果N很大,比如说1000或更多,总体偏差会很小,也相对均匀,并且会非常快。但是,您注意到列表增长缓慢,因此这种方法不可行。

如果您尝试这种方法,您将得到ConcurrentModificationException。如果您想要统一的洗牌,我不希望在物理上有任何更快的洗牌方法。Fisher-Yates洗牌对大小为n的数组执行n次交换操作。我非常怀疑你能在少于这个的时间内完成一个统一的洗牌。我不明白为什么你每次迭代都要洗牌整个列表,而90%的字符串都被忽略了。你不能每次都选择一些随机索引,并且在整个方法结束时只洗牌一次吗?一些评论/答案似乎被你发布的代码误导了-我认为这是伪代码,只是为了说明这个想法。如果您提供了有关实际应用程序案例的更多信息或代码,您可能会给出更好的建议。如果您尝试这样做,您将得到ConcurrentModificationException。如果您想要统一的洗牌,我不希望在物理上有任何更快的洗牌方法。T

Fisher-Yates shuffle对大小为n的数组执行n次交换操作。我非常怀疑你能在少于这个的时间内完成一个统一的洗牌。我不明白为什么你每次迭代都要洗牌整个列表,而90%的字符串都被忽略了。你不能每次都选择一些随机索引,并且在整个方法结束时只洗牌一次吗?一些评论/答案似乎被你发布的代码误导了-我认为这是伪代码,只是为了说明这个想法。如果您提供了关于实际应用程序案例的更多信息或代码,您可能会给出更好的建议。这将实现什么?请注意,棘手的部分不是从非缓冲列表中选取随机元素,而是以随机顺序对每个元素进行一次处理ahhh好的,然后在。。。因此,您可以随机选择元素并将其从列表中丢弃。如果您需要保留原始列表,您可以随时复制您的列表one@PatB马可是对的。我需要以随机顺序处理每个元素一次。ArrayList的删除速度比Collections慢。在如此大的集合上执行shuffle。这将实现什么?请注意,棘手的部分不是从非缓冲列表中选取随机元素,而是以随机顺序对每个元素进行一次处理ahhh好的,然后在。。。因此,您可以随机选择元素并将其从列表中丢弃。如果您需要保留原始列表,您可以随时复制您的列表one@PatB马可是对的。我需要以随机顺序处理每个元素一次。ArrayList的删除速度比Collections.shuffle慢。在如此大的集合上执行shuffle。上面显示的代码只是示例代码。我所实现的实际模型要求始终以足够随机的顺序处理元素,以便模型不会有偏差。@Robotia此模型中没有偏差,因为您会随机决定处理每个元素,这不取决于元素的位置。必须处理每个元素。@Robotia Yes,但是它是否被选中并不取决于它在列表中的位置,只取决于随机调用的结果。我所实现的实际模型要求始终以足够随机的顺序处理元素,以便模型不会有偏差。@Robotia此模型中没有偏差,因为您会随机决定处理每个元素,这不取决于元素的位置。必须处理每个元素。@Robotia Yes,但它是否被选中并不取决于它在列表中的位置,而只取决于随机调用的结果。@Durandal是ArrayList removing O1,用于从列表末尾删除吗?我以为它一直都在大O备忘单上写着。。。明天早上我会测试这个,看起来很有希望!真的很晚了here@Marco13它最终比收集速度慢。shuffle@Marco13我知道你从哪里来,接受了你的建议;将示例更改为将随机实例作为参数。虽然我不认为一方面示例是一个示例并期望应用常识是必要的,但另一方面——提供风格良好的示例同样有效,特别是考虑到SO的受众中哪些是常识。因此,毕竟:合理的方法,但可能不是问题中没有真正解决的实际问题的解决方案-但也许评论中的讨论将有助于解决问题…@Marco13再次为我造成的混乱感到抱歉。@Durandal是否将ArrayList Remove O1从列表末尾删除?我以为它一直都在大O备忘单上写着。。。明天早上我会测试这个,看起来很有希望!真的很晚了here@Marco13它最终比收集速度慢。shuffle@Marco13我知道你从哪里来,接受了你的建议;将示例更改为将随机实例作为参数。虽然我不认为一方面示例是一个示例并期望应用常识是必要的,但另一方面——提供风格良好的示例同样有效,特别是考虑到SO的受众中哪些是常识。因此,毕竟:合理的方法,但可能不是问题中未真正解决的实际问题的解决方案,但可能评论中的讨论将有助于解决问题…@Marco13再次对我造成的混乱表示抱歉。我尝试了Fisher-Yates shuffle vs.Collections.shuffle on Integer[]和ArrayList,有趣的是发现Collections更快。您还可以尝试将Collections.shuffle的src代码应用于int[],并且不要忘记在测量之前及时预热VM,否则第二种算法可能会更快。shuffle的src:我分别在Integer[]和ArrayList上尝试了Fisher-Yates shuffle和Collections.shuffle
有趣的是,nd发现收集速度更快。您还可以尝试将Collections.shuffle的src代码应用于int[],并且不要忘记在测量之前及时预热VM,否则第二种算法可能会更快。洗牌的src: