12.HashSet
1.介绍
HashSet集合底层是哈希表存储数据,哈希表是一种对于增删查改数据性能都较好的结构,在JDK8之前的,底层使用【数组+链表】组成,在JDK8开始后,底层采用【数组+链表 | 红黑树】组成.
2.HashSet保证元素唯一性
-
需要同时重写对象中的hashCode和equals方法
-
hashCode和equals方法的配合流程 :
-
当添加对象的时候, 会先调用对象的hashCode方法计算出一个应该存入的索引位置, 查看该位置上是否存在元素
-
-
不存在:直接存
-
存在:调用equals方法比较内容
false : 存
true : 不存
-
-
-
JDK8以前 : 头插法
JDK8以后 : 尾插法
3.HashSet扩容
-
底层数组扩容
扩容数组的条件 :
A: 当数组中的元素个数到达了 16 * 0.75 (加载因子) = 12
扩容原数组 2 倍的大小
B:链表挂载的元素超过了8 (阈值) 个 , 并且数组长度没有超过64
-
底层链表转红黑树
链表挂载的元素超过了8 (阈值) 个, 并且数组长度到达了64
13.LinkHashSet
- 底层加入了双向链表.
- 有序、不重复、无索引。(这里的有序指的是保证存储和取出的元素顺序一致)
- 原理 : 底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
14.Map集合
1.介绍
- Map集合是一种双列集合,每个元素包含两个数据。
- Map集合的每个元素的格式:key=value(键值对元素),Map集合也被称为“键值对集合”
2.体系集合
3.特点
-
Map集合中底层的数据结构, 只针对键有效 !!!
-
HashMap : 哈希表, key (键) 是唯一的
-
LinkedHashMap : 哈希表 + 链表 , key (键) 唯一, 有序
-
TreeMap : 红黑树, key (键) 排序
4.常用方法
方法名 | 说明 |
---|---|
V put (K key , V value) | 添加元素 |
V remove (Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boole containsKey(Object key) | 判断集合是否包含指定的键 |
boole containsValue(Object value) | 判断集合是否包含指定的值 |
boolea isEmpty() | 判断集合是否为空 |
int size() | 集合中键值对的对数 |
5.put 方法细节
put 方法有修改效果 —>如果键已经存在, 新值会覆盖旧值
6.Map集合的遍历
-
键找值的方式遍历:先获取Map集合全部的键
再根据遍历键找值。//map的第一种遍历,通过键找值 public static void main(String[] args) { HashMap<String ,String> hm = new HashMap<>(); hm.put("阿芷","曲靖"); hm.put("阿芷芷","昆明"); hm.put("Π","云南"); //迭代器遍历 Set<String> keySet = hm.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()){ String key = iterator.next(); String value = hm.get(key); System.out.println(key+"---"+value); } System.out.println("-------------------------------------"); //增强for循环遍历 for (String key : hm.keySet()) { String value = hm.get(key); System.out.println(key+"---"+value); } }
-
键值对的方式遍历,把“键值对“看成一个整体
//map的第二种遍历方式,通过键值对对象 public static void main(String[] args) { HashMap<String ,String> hm = new HashMap<>(); hm.put("阿芷","曲靖"); hm.put("阿芷芷","昆明"); hm.put("Π","云南"); //获取所有的键值对对象 Set<Map.Entry<String, String>> entrySet = hm.entrySet(); //获取每一个键值对对象 for (Map.Entry<String, String> entry : entrySet) { //entry.getKey()---->获取键值对对象中的键 //entry.getValue()-->获取键值对对象中的值 System.out.println(entry.getKey()+ "---" + entry.getValue()); } }
-
通过for each遍历
hm.forEach(new BiConsumer<String, String>() { @Override public void accept(String key, String value) { System.out.println(key + "---" + value); } }); }
7.Map集合的底层原理
15.Collections类常用方法
16.可变参数
-
介绍
可变参数是指,参数的个数可变
-
本质 :
底层是一个数组实现
-
注意 :
可变参数是需要三个点,点的数量不允许改变
不要将 … 写在类型的前面
如果可变参数是方法中的第一个参数,后面将不允许再定义参数,因为接收不到
17.HashSet和HashMap
对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,更确切的说,HashSet中的元素,只是存放在了底层HashMap的key上, 而value使用一个static final的Object对象标识。
18.描述一下HashSet集合数据的添加过程
回答:
当我们添加元素的时候, 添加方法内部会自动调用对象的 hashCode方法, 计算出应存入的索引位置.(底层是调用了HashMap的put方式)
- 计算方式 :
调用hashCode方法得到原始哈希值
对原始哈希值进行哈希扰动, 扰动的方式是向右移动16位
使用扰动后的哈希, 和原始哈希进行 ^ 操作, 这是二次哈希操作
然后使用运算后的哈希值, 和数组的长度进行取余操作, 计算应存入索引位置.
- 但是源码中, 计算方式 (数组长度 - 1) & 哈希值;
- 这样做的原因是 & 操作, 比 % 操作的效率更高.
始哈希值
对原始哈希值进行哈希扰动, 扰动的方式是向右移动16位
使用扰动后的哈希, 和原始哈希进行 ^ 操作, 这是二次哈希操作
然后使用运算后的哈希值, 和数组的长度进行取余操作, 计算应存入索引位置.
- 但是源码中, 计算方式 (数组长度 - 1) & 哈希值;
- 这样做的原因是 & 操作, 比 % 操作的效率更高.
通过阅读Java源代码,发现java的Hashset和HashMap实质上是相同的,HashSet实际上就是hashmap的value值为null 的情况,所以哈HashSet和HashMap有相同的存储元素的形式:无序不可重复。