Hashset背后发现重复数据的难度是多么快?

时间:2018-06-05 10:58:02

标签: java performance duplicates hashset

好的,这可能是一个超级小问题,但是 我有点困惑,渴望听到你能告诉我的事情。

我有一个添加了大约500万个长片的ArrayList。这些long是从一个大的csv文件中计算主键(连接字符串)的哈希值。

现在我想检查唯一性并在列表中循环:

for(int i=0;i<hashArrayList.size();i++)
{
   long refValue = hashArrayList.get(i)
   for(int j=i+1;j<hashArrayList.size();j++)
   {
      if(refValue == hashArrayList.get(j))
      --> UNIQUENESS VIOLATION, now EXPLODE!!
   }
}

这种方式需要HOURS。

现在关于Hashset,它本身不允许重复。 hashset.addAll(hashArrayList)需要4秒!同时消除/不为此列表添加5个mio元素的重复项。

它是如何做到的? 并且:我的ArrayList循环是如此愚蠢吗?

3 个答案:

答案 0 :(得分:3)

你正在做一个完全不同的比较。

使用ArrayList,您有一个嵌套的 for 循环,使其成为O(n^2)

但是使用HashSet,您不会进行任何循环,只需向n添加O(n)个元素即可。在内部,HashSet使用HashMap,其键是列表的各个元素,值是静态 对象

HashSet(Java 8)的源代码

public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

addAll来电add

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

因此,最终将所有对象(此处为 long )插入到HashMap中,该HashMap提供恒定的时间性能 1

1 来自HashMap的javadoc(强调我的

  

此实现为基本操作(get和put)提供了恒定时间性能,假设散列函数在桶之间正确地分散元素

答案 1 :(得分:1)

基于散列的集合不需要循环来检查是否存在具有相同键的元素。

想象一下,你有1000个对象X.在你的情况下,你每次添加东西时都会遍历列表。

基于散列的集合计算对象的散列,查看是否存在具有相同散列的其他元素,然后只需检查其中一个元素是否等于新元素。如果你有一个好的哈希函数,它返回唯一元素的唯一哈希值,你只需要计算一个数字。

当然,如果您只是说“我很懒,而且我使用返回1覆盖我的hashCode方法”,那么除了哈希集合开销之外,您将获得相同数量的比较。

示例:想象一下,您有以下HashSet:

HashSet: [[obj1], [null], [null], [null], [obj2, obj3, obj4]]

如您所见,基本结构(可以)如下所示:包含具有实际条目的其他数据结构的数组。现在,如果你将一个obj5放入HashSet,它将调用obj5.hashCode()。基于此,它将计算该obj的外部索引。让我们说它是4:

HashSet: [[obj1], [null], [null], [null], [obj2, obj3, obj4]]
                                                  ^ obj5

现在我们有三个具有相同索引的其他对象。是的,我们需要一个循环来检查,它们中的一些是否等于新的obj5,但如果你有一个包含数百万个条目的更大的HashSet,与一些元素的比较比与所有元素相比要快得多。这是基于散列的集合的优势。

答案 2 :(得分:0)

Hashmap internal working

此外,你在一个循环中使用循环,这使得复杂度为O(n ^ 2),这比hashmap使用的效率低。