相关文章:
Map集合详解
一、Map集合基本概念
-
Map集合的继承结构图
-
Map集合的特点及其实现类
特点:
(1) Map集合以key和value键值对的方式存储元素
(2) 存储在key中的元素的特点是存取无序,且不能重复存储相同元素实现类:
(1) HashMap
(2) Hashtable
(3) Properties -
Map集合的子接口 ----- SortedMap集合特点及其实现类
特点:
(1) 以key和value键值对的方式存储元素
(2) 存储在key中的元素的特点是存取无序,且不能重复存储相同元素
(3) 集合key部分元素会自动按照大小顺序排序实现类:
TreeMap
二、Map集合常用方法
返回值 | 方法 | 说明 |
---|---|---|
V | put(K key, V value) | 存放键值对 |
V | get(Object key) | 通过key获取value |
V | remove(Object key) | 通过key删除键值对 |
int | size() | 获取集合大小 |
void | clear() | 清空集合 |
boolean | isEmpty() | 判空 |
boolean | containsKey(Object key) | 判断集合中是否包含某个key |
boolean | containsValue(Object value) | 判断集合中是否包含某个value |
Set < K > | keySet() | 把Map集合所有key存放到Set集合中 |
Collection < V > | values() | 把Map集合所有value存放到Collection集合中 |
Set<Map.Entry<K,V>> | entrySet() | 将Map集合转为Set集合 |
注意:
当Map集合对象调用了entrySet()
方法后,其每一个键值对会合并为一个元素(key=value)保存在Set集合中,其中Set集合中的元素数据类型为Map.Entry<K,V>
,其中该类也提供了下面一些方法可以对键值进行获取、修改。
返回值 | 方法 | 说明 |
---|---|---|
K | getKey() | 获取key=value中的key |
V | getValue() | 获取key=value中的value |
V | setValue(V value) | 修改key=value中的value |
Map<Integer, String> map = new HashMap<>();
map.put(1, "YuKee");
map.put(2, "banana");
map.put(3, "DwD");
Set<Map.Entry<Integer, String>> set = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator1 = set.iterator();
while(iterator1.hasNext()) {
System.out.println(iterator1.next());
}
Iterator<Map.Entry<Integer, String>> iterator2 = set.iterator();
while(iterator2.hasNext()) {
System.out.println(iterator2.next().getValue());
}
/*
* 输出:
* 1=YuKee
* 2=banana
* 3=DwD
* YuKee
* banana
* DwD
* */
三、Map集合的两种遍历方式
-
通过key的Set集合来遍历
Map<Integer, String> map = new HashMap<>(); map.put(1, "kl"); map.put(2, "dwd"); map.put(3, "moerdan"); map.put(4, "blue"); Set<Integer> set = map.keySet(); for (Integer i : set) { System.out.println(i + "-->" + map.get(i)); } /* 输出: 1-->kl 2-->dwd 3-->moerdan 4-->blue */
-
将Map集合转换成Set集合,遍历Set集合(效率更高)
Map<Integer, String> map = new HashMap<>(); map.put(1, "YuKee"); map.put(2, "banana"); map.put(3, "DwD"); Set<Map.Entry<Integer, String>> set = map.entrySet(); for (Map.Entry<Integer, String> integerStringEntry : set) { System.out.println(integerStringEntry); } /* * 输出: * 1=YuKee * 2=banana * 3=DwD * */
四、HashMap集合详解
-
关于哈希表数据结构
(1) 哈希表是数组和单向链表的结合体,其底层是一个一维数组,数组中的每一个元素又是一个单向链表。
(2) 哈希表同时具有数组和单向链表的优点:查询效率和随机增删元素效率均较高。
-
HashMap底层就是一个哈希表,其链表部分中的每一个节点(HashMap中的元素)具有以下属性
(1)int hash
,哈希值
(2)K key
,HashMap中的key
(3)V value
,HashMap中的value
(4)Node<K, V> next
,保存下一个节点的地址 -
HashMap中
put(K key, V value)
方法底层原理- 先将key,value封装到Node对象中。
- 底层会调用
key的hashCode()
方法得出hash值,然后通过哈希函数/算法,将哈希值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。 - 如果下标对应的位置上有链表,此时会拿着Node对象的key和链表上的每一个节点的key进行
equals()
比较,如果所有的equals()方法返回false,那么这个新节点将被添加到链表的末尾,如果其中有一个equals()方法返回了true,那么这个节点的value会被新节点的value覆盖。
-
HashMap中的
get(K key)
方法底层原理- 先调用key的
hashCode()
方法得出hash值,然后通过哈希函数/算法,将哈希值转换成数组的下标,通过这个下标快速定位到数组的对应下标位置。 - 如果这个位置上没有链表,则返回null,反之则会拿着这个传入的key和链表上的每一个节点上的key进行
equals()
比较。 - 如果所有equals方法返回false,则get方法返回null,如果有一个equals方法返回true,则get方法就返回对应节点的value。
- 先调用key的
-
注意
(1) HashMap的key部分可以为null。
(2) 同一个链表上的每一个节点(元素)hash值相同,因为其对应的数组下标相同。
(3) 我们在使用HashMap集合对元素进行增删查找时,会先后调用元素的
hashCode()
和equals()
方法,所以对于自定义的数据类型使用HashMap存储时,我们必须要重写其hashCode()
和equals()
方法。(4) HashMap的key部分存取元素无序的原因:不同元素调用hashCode()方法转换成的数组下标无规律,所以这些元素是随机挂在了对应的链表上。
(5) HashMap的key部分元素不可重复的原因:元素中的equals()方法保证了不可重复,只要返回true则会覆盖掉原来的value值。
(6) HashSet本质上就是HashMap的key部分,因此其存取元素无序,存储元素不可重复的原因和上面相同,而且对应HashSet集合中存储的自定义数据类型也需要重写
hashCode()
和equals()
方法。(7) 假设将待存取的元素的
hashCode()
方法返回值设置为固定值,那么所有元素都只会存储在哈希表底层下数组的同一个下标位置上,这样这个哈希表就变成了一个单向链表,无法发挥其作用了,想要发挥哈希表的作用,就必须让存取的元素散列均匀分布在底层数组的不同位置上,这也是我们需要重写自定义数据类型的hashCode()
方法的原因。同理,如果让hashCode()
方法返回值全都不同,那么哈希表就变成了一个一维数组。(8) 优化:HashMap初始化容量必须是2的倍数(默认16),有利于让存储的元素散列分布均匀,提高存取效率。
五、重写自定义数据类型的hashCode()和equals()方法
使用IDEA,想让哪几个属性作为判断元素的依据则在生成方法时勾选上即可
public class Student {
private int num;
private String name;
public Student(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return num == student.num &&
name.equals(student.name);
}
@Override
public int hashCode() {
return Objects.hash(num, name);
}
}
结论:存放在HashMap的key部分的元素,以及存放在HashSet中的元素,需要同时重写hashCode()
和equals()
方法