HashSet与HashMap的区别
来源:互联网 发布:欧洲专利局数据库 编辑:程序博客网 时间:2024/05/17 22:24
在网上找了好久终于找到了详尽的解释,记录下来备忘。。
HashSet 的实现
对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码:
- publicclass HashSet<E>
- extends AbstractSet<E>
- implements Set<E>, Cloneable, java.io.Serializable
- {
- // 使用 HashMap 的 key 保存 HashSet 中所有元素
- privatetransient HashMap<E,Object> map;
- // 定义一个虚拟的 Object 对象作为 HashMap 的 value
- privatestatic final Object PRESENT =new Object();
- ...
- // 初始化 HashSet,底层会初始化一个 HashMap
- public HashSet()
- {
- map = new HashMap<E,Object>();
- }
- // 以指定的 initialCapacity、loadFactor 创建 HashSet
- // 其实就是以相应的参数创建 HashMap
- public HashSet(int initialCapacity,float loadFactor)
- {
- map = new HashMap<E,Object>(initialCapacity, loadFactor);
- }
- public HashSet(int initialCapacity)
- {
- map = new HashMap<E,Object>(initialCapacity);
- }
- HashSet(int initialCapacity,float loadFactor, boolean dummy)
- {
- map = new LinkedHashMap<E,Object>(initialCapacity
- , loadFactor);
- }
- // 调用 map 的 keySet 来返回所有的 key
- public Iterator<E> iterator()
- {
- return map.keySet().iterator();
- }
- // 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数
- publicint size()
- {
- return map.size();
- }
- // 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,
- // 当 HashMap 为空时,对应的 HashSet 也为空
- publicboolean isEmpty()
- {
- return map.isEmpty();
- }
- // 调用 HashMap 的 containsKey 判断是否包含指定 key
- //HashSet 的所有元素就是通过 HashMap 的 key 来保存的
- publicboolean contains(Object o)
- {
- return map.containsKey(o);
- }
- // 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap
- publicboolean add(E e)
- {
- return map.put(e, PRESENT) ==null;
- }
- // 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素
- publicboolean remove(Object o)
- {
- return map.remove(o)==PRESENT;
- }
- // 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素
- publicvoid clear()
- {
- map.clear();
- }
- ...
- }
由上面源程序可以看出,HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
- class Name
- {
- private String first;
- private String last;
- public Name(String first, String last)
- {
- this.first = first;
- this.last = last;
- }
- publicboolean equals(Object o)
- {
- if (this == o)
- {
- returntrue;
- }
- if (o.getClass() == Name.class)
- {
- Name n = (Name)o;
- return n.first.equals(first)
- && n.last.equals(last);
- }
- returnfalse;
- }
- }
- publicclass HashSetTest
- {
- publicstatic void main(String[] args)
- {
- Set<Name> s = new HashSet<Name>();
- s.add(new Name("abc","123"));
- System.out.println(
- s.contains(new Name("abc","123")));
- }
- }
上面程序中提供了一个 Name 类,该 Name 类重写了 equals() 和 toString() 两个方法,这两个方法都是根据 Name 类的 first 实例变量来判断的,当两个 Name 对象的 first 实例变量相等时,这两个 Name 对象的 hashCode() 返回值也相同,通过 equals() 比较也会返回 true。
程序主方法先将第一个 Name 对象添加到 HashSet 中,该 Name 对象的 first 实例变量值为"abc",接着程序再次试图将一个 first 为"abc"的 Name 对象添加到 HashSet 中,很明显,此时没法将新的 Name 对象添加到该 HashSet 中,因为此处试图添加的 Name 对象的 first 也是" abc",HashSet 会判断此处新增的 Name 对象与原有的 Name 对象相同,因此无法添加进入,程序在①号代码处输出 set 集合时将看到该集合里只包含一个 Name 对象,就是第一个、last 为"123"的 Name 对象
正确的使用HashMap
1:不要在并发场景中使用HashMap
HashMap是线程不安全的,如果被多个线程共享的操作,将会引发不可预知的问题,据sun的说法,在扩容时,会引起链表的闭环,在get元素时,就会无限循环,后果是cpu100%。
看看get方法的红色部分
- public V get(Object key) {
- if (key == null)
- return getForNullKey();
- int hash = hash(key.hashCode());
- for (Entry<K,V> e = table[indexFor(hash, table.length)];
- e != null;
- e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
- return e.value;
- }
- return null;
- }
2:如果数据大小是固定的,那么最好给HashMap设定一个合理的容量值
根据上面的分析,HashMap的初始默认容量是16,默认加载因子是0.75,也就是说,如果采用HashMap的默认构造函数,当增加数据时,数据实际容量超过10*0.75=12时,HashMap就扩容,扩容带来一系列的运算,新建一个是原来容量2倍的数组,对原有元素全部重新哈希,如果你的数据有几千几万个,而用默认的HashMap构造函数,那结果是非常悲剧的,因为HashMap不断扩容,不断哈希,在使用HashMap的场景里,不会是多个线程共享一个HashMap,除非对HashMap包装并同步,由此产生的内存开销和cpu开销在某些情况下可能是致命的。
- HashSet与HashMap的区别
- HashSet与HashMap的区别
- HashMap与HashSet的区别
- HashSet与HashMap的区别
- HashMap与HashSet区别
- HashSet与HashMap的区别与联系
- Map与HashMap,Hashtable,HashSet的区别
- Map与HashMap,Hashtable,HashSet的区别
- Map与HashMap,Hashtable,HashSet的区别
- java 中hashset 与hashmap的区别
- HashMap与HashTable的区别 |HashSet和HashMap的区别
- HashMap与HashTable的区别、HashMap与HashSet的关系
- HashMap与HashTable的区别、HashMap与HashSet的关系
- HashMap与HashTable的区别、HashMap与HashSet的关系
- HashMap与HashTable的区别、HashMap与HashSet的关系
- hashset,hashmap的区别
- HashSet与HashTable hashMap区别
- HashSet,HashMap与HashTable的关系与区别
- VC中check box的两种用法
- 再再谈先行工作流-终结篇
- java 字节流与字符流的区别
- 面试题总结-processbar
- PI的求法
- HashSet与HashMap的区别
- 第一章作业
- Statement批量执行sql语句,批量操作数据库,提高数据库效率
- 简单易用的Rest
- WinCE Subproject使用介绍
- android中各种传感器的应用与编程实例
- Socket通信原理和实践
- 侯捷的职业建议
- Thread类run()方法和start()方法区别