乔恩·斯凯特(Jon Skeet)最近在他的博客上提出了一个有趣的编程主题:“我的抽象中有一个漏洞,亲爱的Liza,亲爱的Liza”(强调):
我有一套-
HashSet
实际上。我想从中删除一些项目……许多项目可能不存在。实际上,在我们的测试案例中,“删除”集合中的所有项目都不在原始集中。这听起来(确实是)非常容易编码。毕竟,我们Set<T>.removeAll
需要帮助我们,对吧?我们在命令行上指定“源”集的大小和“删除”集合的大小,然后构建它们两者。源集仅包含非负整数;清除集仅包含负整数。我们将使用来测量删除所有元素所需的时间
System.currentTimeMillis()
,这并不是世界上最精确的秒表,但是在这种情况下,这已经足够了。这是代码:import java.util.*; public class Test { public static void main(String[] args) { int sourceSize = Integer.parseInt(args[0]); int removalsSize = Integer.parseInt(args[1]); Set<Integer> source = new HashSet<Integer>(); Collection<Integer> removals = new ArrayList<Integer>(); for (int i = 0; i < sourceSize; i++) { source.add(i); } for (int i = 1; i <= removalsSize; i++) { removals.add(-i); } long start = System.currentTimeMillis(); source.removeAll(removals); long end = System.currentTimeMillis(); System.out.println("Time taken: " + (end - start) + "ms"); } }
让我们开始做一个简单的工作:一个源集100个项目,要删除的100个项目:
c:UsersJonTest>java Test 100 100 Time taken: 1ms
好的,所以我们没想到它会变慢……很明显,我们可以将事情加速进行。一百万个项目和30万个要删除的项目的来源怎么样?
c:UsersJonTest>java Test 1000000 300000 Time taken: 38ms
嗯 看起来还是很迅速的。现在,我觉得自己有点残酷,要求它执行所有删除操作。让我们简化一点– 300,000个源项目和300,000个清除项目:
c:UsersJonTest>java Test 300000 300000 Time taken: 178131ms
对不起?近三分钟?kes!当然,从一个较小的集合中删除项目应该比我们在38毫秒内管理的项目更容易吗?
有人可以解释为什么会这样吗?为什么HashSet<T>.removeAll
方法这么慢?