博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java集合之HashSet源码分析
阅读量:6860 次
发布时间:2019-06-26

本文共 5352 字,大约阅读时间需要 17 分钟。

一、HashSet简介

  HashSet是Set接口典型实现,它按照Hash算法来存储集合中的元素,具有很好的存取和查找性能。主要具有以下特点:

  • 不保证set的迭代顺序
  • HashSet不是同步的,如果多个线程同时访问一个HashSet,要通过代码来保证其同步
  • 集合元素值可以是null

  当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该值确定对象在HashSet中的存储位置。在Hash集合中,不能同时存放两个相等的元素,而判断两个元素相等的标准是两个对象通过equals方法比较相等并且两个对象的HashCode方法返回值也相等。

  下面的例子说明了上述特性:

public class Person{    String name;    int age;        public Person(String name,int age)    {        this.name=name;        this.age=age;    }        public String getName()    {        return name;    }    public void setName(String name)    {        this.name = name;    }    public int getAge()    {        return age;    }    public void setAge(int age)    {        this.age = age;    }    //当对象的名字和姓名相同即返回true    public boolean equals(Object obj)    {        if(obj==null)            return false;        if((this.name.equals(((Person)obj).name) && this.age==((Person)obj).age))                return true;        else            return false;    }    }

  此时添加两个name和age均相同的Person对象实例到HashSet中:

public class HashSetDemo{    public static void main(String[] args)    {        HashSet
hs = new HashSet<>(); Person p1=new Person("xujian", 23); Person p2=new Person("xujian", 23); hs.add(p1); hs.add(p2); for(Person p:hs) { System.out.println(p.name+"---"+p.age); } }}

  

  可见,HashSet中存放了两个name和age均相同的Person对象。

  接下来我们重写一下Person类的hashCode方法,使其返回相同的HashCode。

public class Person{    String name;    int age;        public Person(String name,int age)    {        this.name=name;        this.age=age;    }    public String getName()    {        return name;    }    public void setName(String name)    {        this.name = name;    }    public int getAge()    {        return age;    }    public void setAge(int age)    {        this.age = age;    }    public int hashCode()    {        // TODO 自动生成的方法存根        return 1;    }    //当对象的名字和姓名相同即返回true    public boolean equals(Object obj)    {        if(obj==null)            return false;        if((this.name.equals(((Person)obj).name) && this.age== ((Person)obj).age))                return true;        else            return false;    }    }

  再次执行向HashSet添加元素操作,会发现此时HashSet只保存了一个。

  

  HashSet中每一能存储元素的槽位通常称为“桶”,如果有多个元素的hashCode相同,但是通过equals方法比较返回false,就需要在一个桶上存放多个元素。

二、HashSet源码分析

  1、构造函数

  HashSet的底层实际上是由HashMap实现的。其四个构造函数分别对应相应的HashMap。

//构造一个新的,空的HashSet,其底层 HashMap实例的默认初始容量是 16,加载因子是 0.75    public HashSet()     {        map = new HashMap<>();    }    //构造一个包含指定 collection 中的元素的新 set    public HashSet(Collection
c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } //构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } //构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子0.75 public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); }

  2、HashSet常用方法

  boolean add(E e): 如果此 set 中尚未包含指定元素,则添加指定元素

public boolean add(E e)     {        //调用map的put方法,其中value值为静态的Object对象        return map.put(e, PRESENT)==null;    }

  void clear():从此 set 中移除所有元素

public void clear()     {        map.clear();    }

  Object clone():返回此 HashSet 实例的浅表副本

public Object clone()    {        try         {            //调用父类的clone方法            HashSet
newSet = (HashSet
) super.clone(); newSet.map = (HashMap
) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(e); } }

  boolean contains(Object o):如果此 set 包含指定元素,则返回 true

public boolean contains(Object o)     {        return map.containsKey(o);    }

  boolean isEmpty():如果此 set 不包含任何元素,则返回 true

public boolean isEmpty()     {        return map.isEmpty();    }

  Iterator<E> iterator():返回对此 set 中元素进行迭代的迭代器

public Iterator
iterator() { return map.keySet().iterator(); }

  boolean remove(Object o):如果指定元素存在于此 set 中,则将其移除

public boolean remove(Object o)     {        return map.remove(o)==PRESENT;    }

  int size():返回此 set 中的元素的数量

public int size()     {        return map.size();    }

三、HashSet的应用示例代码

public class HashSetDemo{    public static void main(String[] args)    {        HashSet
hs1 = new HashSet<>(); //无参构造函数新建一个默认大小为16,装载因子为0.75的HashSet System.out.println("调用add函数"); hs1.add("Hello"); hs1.add("World"); hs1.add("nihao"); HashSet
hs2 = new HashSet<>(hs1); //构造一个包含hs1中元素的HashSet System.out.println("调用remove函数"); hs1.remove("Hello"); for(String str:hs1) System.out.println(str); System.out.println("调用clone函数"); HashSet
hs3=(HashSet
) hs2.clone(); for(String str:hs3) System.out.println(str); System.out.println("利用迭代器遍历HashSet中元素"); Iterator
it=hs2.iterator(); while(it.hasNext()) { System.out.println(it.next()); } System.out.println("调用size函数"); System.out.print(hs2.size()); }}

  执行结果如图:

  

Java集合系列:

    

       

   

转载地址:http://cbxyl.baihongyu.com/

你可能感兴趣的文章
HTTP慢速DOS(slow http denial of service attack)
查看>>
图片水印
查看>>
Quart2D的基本介绍
查看>>
Lua点号和冒号区别
查看>>
STL基础
查看>>
有没有人要贪吃蛇的代码,然而有....................
查看>>
Spring Data JPA 进阶
查看>>
linux的top命令参数详解
查看>>
spring常用注解
查看>>
Material Menu
查看>>
Win8 环境变量位置
查看>>
margin负值的应用总结
查看>>
分页查询
查看>>
iLBC
查看>>
Linux 本地yum源搭建和网络yum源搭建
查看>>
HDU 5781 ATM Mechine
查看>>
使用Nginx搭建Tomcat9集群,Redis实现Session共享
查看>>
Extjs4.1 序列化和反序列化
查看>>
git 最常用命令
查看>>
iOS self 和 super 学习
查看>>