HashSet

 

 

什么是HashSet

HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。

public boolean add(Object o)方法用来在Set中添加元素,当元素值重复时则会立即返回false,如果成功添加的话会返回true。

什么是HashMap

HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,能保证只有一个线程更改Map。

public Object put(Object Key,Object value)方法用来将元素添加到map中。

HashSet和HashMap的区别

HashMapHashSet
HashMap实现了Map接口HashSet实现了Set接口
HashMap储存键值对HashSet仅仅存储对象
使用put()方法将元素放入map中使用add()方法将元素放入set中
HashMap中使用键对象来计算hashcode值HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false

为什么要用HahSet?

在我们进行遍历的时候假如想要在一大堆数据中查找X数据。LinkedList的数据结构就不说了,查找效率低的可怕。ArrayList,如果我们不知道X的位置序号,还是一样要全部遍历一次直到查到结果,效率一样比较低。HashSet就是为了提高查找效率的。

为什么HashSet查找效率提高?

   HashSet的add机制后,直接根据数据的散列码和散列表的数组大小计算除余后,就得到了所在数组的位置,然后再查找链表中是否有这个数据即可。查找的代价也就是在链表中,但是真正一条链表中的数据很少,有的甚至没有。几乎没有什么迭代的代价可言了。所以散列表的查找效率建立在散列单元所指向的链表中的数据要少 。

   HashSet不能添加重复的元素,当调用add(Object)方法时候,
首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;
如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。

总结一下HashSet的特点:

1、HashSet不能重复存储equals相同的数据 。原因就是equals相同,数据的散列码也就相同(hashCode必须和equals兼容)。大量相同的数据将存放在同一个散列单元所指向的链表中,造成严重的散列冲突,对查找效率是灾难性的。

2、HashSet的存储是无序的 ,没有前后关系,他并不是线性结构的集合。

3、hashCode必须和equals必须兼容

自己定义了一个类,想对这个类的大量对象组织成散列表结构便于查找。有一点一定要注意:就是hashCode方法必须与equals方法向兼容。

//hashCode与equals方法的兼容   
public class Employee{   
       public int id;   
       public String name="";   
       //相同id对象具有相同散列码   
       public int hashCode(){    
              return id;   
       }   
       //equals必须比较id   
        public boolean equals(Employee x){   
              if(this.id==x.id) return true;   
              else return false;   
       }   
}  

      为什么要这样,因为HashSet不允许相同元素(equals==ture)同时存在在结构中。假如employeeX(1111,“张三”)和employee(1111,"李四"),而Employee.equals比较的是name。这样的话,employeeX和employeeY的equals不相等。它们会根据相同的散列码1111加入到同一个散列单元所指向的列表中。这种情况多了,链表的数据将很庞大,散列冲突将非常严重,查找效率会大幅度的降低。

因为Java关于hashcode的规定是,equal相等那么hashcode一定相等,如果hashcode相等equal不一定相等。而equal默认比的是地址,如果重写了equal,重写成比较对象的某个值相等,那么就回出现类似于equal相等,但hashcode不相等的情况,这明显是逻辑不一定,在调用set集合的时候也会出现重复的对象放到了集合中。

       下面的代码是验证hashset  add的时候hashcode和equal的调用顺序:

/**
 * @ Author zhangsf
 * @CreateTime 2019/3/5 - 3:46 PM
 */
package com.bjut;
import java.util.HashSet;
class HashSet_add {
    public static void main(String[] args)  {
        HashSet aa = new HashSet();
        A a1 = new A();
        B b1 = new B();
//        aa.add(a1);
        boolean flag1 =aa.add(a1);
        System.out.println("flag----1"+flag1);
//        aa.add(b1);
        boolean flag2 =aa.add(b1);
        System.out.println("flag----2"+flag2);
        System.out.println(aa);

    }
}
class A{
    private static final String String = null;
    public int a;
    public int b;
    public boolean equals(Object obj) {
        System.out.println("A_equals");
        return false;
    }
    public int hashCode(){
        System.out.println("A_hashcode");
        return 1;
    }
    public String toString(){
        System.out.println("A");
        return "A";
    }
}
class B{
    public int a;
    public int b;
    public boolean equals(Object obj){
        System.out.println("B_equals");
        return true;
    }
    public int hashCode(){
        System.out.println("B_hashcode");
        return 1;
    }
    public String toString(){
        System.out.println("B");
        return "B";
    }
}

运行结果为:

A_hashcode
flag----1true
B_hashcode
B_equals
flag----2false
A
[A]

可以看出在运行add方法的时候的时候

HashSet中add的时候,会先比较hashcode是否相同,如果hashcode不同的话,直接写进去,如果相同的话会比较equals方法。

hashCode必须和equals必须兼容。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页