HashSet
来源:互联网 发布:娶网络女主播 编辑:程序博客网 时间:2024/05/29 07:37
HashSet 是 Set接口的经典实现,它按Hash算法来存储集合中的元素,因此具有很好的存储和查找性能。HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT = new Object();。HashSet跟HashMap一样,都是一个存放链表的数组。
HashSet具有以下特点:
1.不能保证元素的排列顺序,顺序可能与元素的添加顺序不同,元素的顺序可能变化。
2.HashSet不是同步的,如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,必须通过代码来保证其同步。
3.集合元素值可以是null
实现关系
HashSet判断两个元素相等的两个标准
1.两个对象通过equals()方法比较相等。
2.两个对象的hashCode返回值相等。
// 类A的equals方法总是返回true,但没有重写其hashCode()方法class A{ public boolean equals(Object obj) { return true; }}// 类B的hashCode()方法总是返回1,但没有重写其equals()方法class B{ public int hashCode() { return 1; }}// 类C的hashCode()方法总是返回2,且重写其equals()方法总是返回trueclass C{ public int hashCode() { return 2; } public boolean equals(Object obj) { return true; }}public class HashSet1 { public static void main(String[] args) { HashSet books = new HashSet(); // 分别向books集合中添加两个A对象,两个B对象,两个C对象 Boolean A1 = books.add(new A()); Boolean A2 = books.add(new A()); Boolean B1 = books.add(new B()); Boolean B2 = books.add(new B()); Boolean C1 = books.add(new C()); Boolean C2 = books.add(new C()); /*当HashSet中有相同对象时add()方法会返回false*/ System.out.println("A1:"+A1+" A2:"+A2+"\nB1:"+B1+" B2:"+B2+"\nC1:"+C1+" C2:"+C2); }}
输出结果
A1:true A2:trueB1:true B2:trueC1:true C2:false
两个A对象添加成功,因为即使两个A对象通过equals()方法比较返回true,但是HashSet依然把他们当成两个对象。
两个B对象添加成功,因为即使两个B对象的hashCode返回相同值(都是1),但是HashSet依然把他们当成两个对象。
两个C对象一个添加成功,一个添加失败是因为当第一个C对象添加成功后books里面已经有一个C对象,无法再添加第二个相同的对象。
注意:
1.如果两个对象通过equals()方法比较返回true,但这两个对象的hashCode方法返回不同的hashCode值时,这将导致HashSet会把这两个对象保存在Hash表的不同位置,从而使两个对象都可以添加成功。
2.如果两个对象的hashCode()方法返回的hashCode值相同,但它们通过equals 方法比较返回false时将会出现:两个对象的hashCode值相同,HashSet将试图把它们保存在同一个位置,但是又不行(否则将只剩下一个对象),所以实际上会在这个位置用链式结构来保存多个对象;而HashSet访问集合元素式是通过元素的hashCode值快速定位的,如果HashSet中两个以上的元素具有相同的hashCode值,将导致性能下降。
重写hashCode()方法的基本规则
- 在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。
- 当两个对象通过equals()方法比较返回true时,这两个对象的hashCode()方法应该返回相同的值。
- 对象中用做equals()方法比较标准的实例变量,都应该用于计算hashCode。
hashCode=(int)(l^(l>>>32)) 普通引用类型 hashCode=f.hashCode()
注意
如果向HashSet中添加一个可变对象后,后面程序修改了该可变对象的实例变量,则可能导致它与集合中的其他元素相同(即两个对象通过equals()方法比较返回true,两个对象的hashCode值也相等),这就有可能导致HashSet中包含两个相同的对象。
package com.bluedot.Collection;/** * @Author: WFP * @Time: 2017/9/23 * Collection * HashSet测试javabean对象 */public class HashSetObj { private int count; public int getCount() { return count; } public void setCount(int count) { this.count = count; } public HashSetObj(int count) { this.count = count; } /*最好重写toString方法,不然Stream API中输出的是该对象的ClassName*/ @Override public String toString() { return "HashSetObj{" + "count=" + count + '}'; } /*重写equals()方法*/ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof HashSetObj)) return false; HashSetObj that = (HashSetObj) o; return count == that.count; } /*重写hashCode方法,把对象内每个有意义的实例变量计算出一个int类型的hashCode值*/ @Override public int hashCode() { return count; }}
package com.bluedot.Collection;import org.junit.Test;import java.util.HashSet;import java.util.Optional;/** * @Author: WFP * @Time: 2017/9/23 * Collection */public class TestHashSet { @Test public void test1(){ HashSet hs = new HashSet(); hs.add(new HashSetObj(5)); hs.add(new HashSetObj(-3)); hs.add(new HashSetObj(9)); hs.add(new HashSetObj(-2)); /*使用Stream API中的forEach方法遍历输出该HashSet*/ hs.stream() .forEach(System.out::println); System.out.println("***********************"); /*使用Stream API中的findFirst方法取出流中的第一个元素,注意findFirst方法返回的是Option*/ Optional<HashSetObj> first = hs.stream() .findFirst(); /*为第一个元素的count赋值*/ first.get().setCount(-3); /*再次输出HashSet,集合中有重复元素*/ hs.stream() .forEach(System.out::println); System.out.println("***********************"); /*删除count为-3的HashSetObj对象*/ hs.remove(new HashSetObj(-3)); /*再次输出HashSet,可以看见被删除了一个HashSet对象*/ hs.stream() .forEach(System.out::println); System.out.println("***********************"); System.out.println("hs是否包含了count为-3的HashSetObj对象?"+hs.contains(new HashSetObj(-3))); System.out.println("hs是否包含了count为-2的HashSetObj对象?"+hs.contains(new HashSetObj(-2))); }}
输出结果
HashSetObj{count=-2}HashSetObj{count=-3}HashSetObj{count=5}HashSetObj{count=9}***********************HashSetObj{count=-3}HashSetObj{count=-3}HashSetObj{count=5}HashSetObj{count=9}***********************HashSetObj{count=-3}HashSetObj{count=5}HashSetObj{count=9}***********************hs是否包含了count为-3的HashSetObj对象?falsehs是否包含了count为-2的HashSetObj对象?false
所以当程序把可变对象添加到HashSet中后,尽量不要去修改集合元素中参加计算hashCode()、equals()的实例变量,否则将会导致HashSet无法正确操作集合元素。
- HashSet
- Hashset
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- HashSet
- hashset
- HashSet
- tarantool知识整理(1)
- 机器学习(周志华西瓜书)参考答案总目录
- Python基本类型
- 驱动中获取PsActiveProcessHead变量地址的五种方法
- 网络与多媒体基础知识--软考
- HashSet
- 网页登录以及单点登录的一些概念
- Ajax -- 发送 POST 请求
- printf和sprintf的小知识
- 如何做网课才可以更好地变现?
- 笔记:Linux环境下lua脚本层使用protobuf
- [tyvj1064]新三国争霸(最短路+dp)
- 关于document.getElementById回去的是什么数据
- Javascript实现继承的几种方式