HashSet在java中的工作原理

时间:2020-02-23 14:34:14  来源:igfitidea点击:

在本教程中,我们将了解java中的Hashset

Java HashSet:

这是corejava访谈中的一个常见问题,因此在本教程中,我们将看到HashSet是如何工作的java.
我们已经了解了hashMap在java中的工作方式,以及hashMap和HashSet之间的区别。
首先让我们看看Hashset的介绍,然后我们将了解它的内部结构。

HashSet 哈希集:

HashSet实现不允许重复的Set接口value.It 不是同步的,也不是线程安全的。
重复的定义可能相当棘手sometimes.Lets 考虑两个案例。

-对于基元类型(如integer、String)
-对于自定义对象。

对于基元类型:

对于primitives类型,它非常直接。让我们举个例子看看:
让我们创建一个java程序:

package org.arpit.theitroad;
import java.util.HashSet;
 
public class HashSetMain {
 
 public static void main(String[] args) {
  HashSet nameSet=new HashSet();
  nameSet.add("Arpit");
  nameSet.add("Arpit");
  nameSet.add("john");
  System.out.println("size of nameSet="+nameSet.size());
  System.out.println(nameSet);
 }
 
}

运行上述程序时,将得到以下输出:

size of nameSet=2
[Arpit, john]

所以我们尝试了两次添加字符串“Arpit”,但由于HashSet不允许重复值,它将在HashSet中添加一次“Arpit”
对于自定义对象:为了理解HashSet在自定义对象中的工作方式,您需要理解java中的hashcode和equals方法。
让我们创建一个名为Country的类,并在其中实现equals方法。

package org.arpit.theitroad;
 
public class Country {
 
 String name;
 long population;
 public String getName() {
  return name;
  }
 public void setName(String name){
  this.name = name;
 }
 public long getPopulation() {
  return population;
 }
 public void setPopulation(long population) {
  this.population = population;
 }
 
 public String toString()
 {
  return name;
 }
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Country other = (Country) obj;
  if (name == null) {
   if (other.name != null)
    return false;
  } else if (!name.equals(other.name))
   return false;
  return true;
 }
 
}

创建 main 类:

package org.arpit.theitroad;
 
import java.util.HashSet;
 
public class HashSetCountryMain {
 
 public static void main(String[] args)
 {
  HashSet countrySet=new HashSet();
  Country india1=new Country();
  india1.setName("India");
 
  Country india2=new Country();
  india2.setName("India");
  
  countrySet.add(india1);
  countrySet.add(india2);
   
  System.out.println("size of nameSet="+countrySet.size());
  System.out.println(countrySet);
  
 }
}

运行上述程序时,将得到以下输出:

size of nameSet=2
[India, India]

现在您一定想知道,即使两个对象相等,为什么HashSet包含两个值而不是一个。这个是因为第一个HashSet为那个key对象计算hashcode,如果hashcode相同,那么它只检查equals方法,并且因为上面两个country对象的hashcode使用默认的hashcode方法,两者都有不同的内存地址,因此哈希代码也不同。现在让我们在上面的Country类中添加hashcode方法

@Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }

Run above main program again, you will get following output:

size of nameSet=1
[India]

现在我们已经很好地理解了HashSet,让我们看看它的内部表示:

哈希集的内部工作:

当您向HashSet添加任何重复元素时,add()方法返回false,并且不向HashSet添加重复元素。
如何添加方法返回false?为此,我们需要在JavaAPI中查看HashSet的add方法

public class HashSet
    extends AbstractSet
    implements Set, Cloneable, java.io.Serializable
{
    
    private transient HashMap<E,Object> map;
 
    // PRESENT is dummy value which will be used as value in map
    private static final Object PRESENT = new Object();
    
    /**
     * Constructs a empty map.so hash
     * 
     */
    public HashSet() {
     map = new HashMap<E,Object>();
    }
    
    // return false if e is already present in HashSet
    public boolean add(E e) {
     return map.put(e, PRESENT)==null;
    }
    
    // other HashSet methods
}

因此,从上面的代码可以清楚地看出,HashSet使用hashMap检查重复的元素。
我们知道,在HashMap中,键应该是唯一的。所以HashSet使用这个概念,当元素被添加到HashSet中时,它作为Key.This HashMap需要一些值,因此在这个HashMap中使用一个伪对象(PRESENT)作为值。PRESENT是用于内部映射的虚拟值。请参见添加方法:

// return false if e is already present in HashSet
    public boolean add(E e) {
     return map.put(e, PRESENT)==null;
    }

所以这里会有两个情况

  • map.put(e,PRESENT)将返回null,如果元素不在该映射中。所以呢地图输入(e,PRESENT)==null将返回true,因此add方法将返回true,元素将添加到HashSet中。
  • map.put(e,PRESENT)将返回旧值,如果元素已经存在于该映射中。所以呢地图输入(e,PRESENT)==null将返回false,因此add方法将返回false,元素将不会添加到HashSet中。