Map接口
1. Map概述
Map:使用键值对(key-value)存储。key 是无序的、不可重复的,是使用了Set来存储所有的key,Key所在类要重写equals()和hashCode()方法。value 是无序的、可重复的,使用Collection来存储所有的value,value所在类要重写equals()方法。
Map接口常用的实现类为HashMap、LinkedHashMap、TreeMap
2. HashMap
2.1 HashMap的底层数据结构
在jdk1.7之前:数组+链表
在jdk1.8时:数组+链表+红黑树
注意: HashMap 最多只允许一条记录的键为 null,允许多条记录的值为 null。
2.2 HashMap底层实现原理(基于jdk1.8)
①HashMap底层的存储结构
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // 存储key的hashcode
final K key; // key不可变
V value; // value
Node<K,V> next; // 指向下一个结点的引用
}
②HashMap的put()方法
当首次调用put()方法时,底层会创建长度为16的Node[]数组,然后将数据添加到哈希表中。
当非首次调用put()方法时,元素的插入过程如下:

③HashMap的扩容问题
当不断的put()数据到HashMap中,出现数组的中的数据个数 > 临界值 ,就会进行扩容。扩容的规则:扩容为原有容量的2倍。
④HashMap中的链表转化为红黑树
当数组中的某一个索引位置的元素以链表形式存在的个数 >= 8 且 当前数组的个数 > 64 时,此索引位置中的所有元素使用红黑树存储。如果数组的个数 < 64 时,对数组进行扩容。
3. LinkedHashMap
① LinkedHashMap底层存储结构
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
从上面的代码可知底层数据结构为:HashMap + 双向链表

从上面的图可知:LinkedHashMap结点中维护了一对前后指针,按照插入顺序进行连接,所以LinkedHashMap能够按照插入顺序遍历。
public class HashMapTest {
public static void main(String[] args) {
Map<Integer, String> map1 = new LinkedHashMap<>();
map1.put(3, "c");
map1.put(1, "a");
map1.put(2, "b");
Set<Map.Entry<Integer, String>> entrySet1 = map1.entrySet();
for (Map.Entry entry : entrySet1){
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}
控制台的输出:

4. TreeMap
TreeMap 实现 SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用 Iterator 遍历 TreeMap 时,得到的记录是排过序的。在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的Comparator。
①TreeMap的底层数据结构
TreeMap存储键值对,通过红黑树实现。由于采取红黑树实现,默认情况下通过key值的自然排序进行排序。
private static final boolean RED = false;
private static final boolean BLACK = true;
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left; // 左孩子结点
Entry<K,V> right; // 右孩子结点
Entry<K,V> parent; // 父节点
boolean color = BLACK; // 红色或者黑色
}
②实现Comparable接口进行排序
public class Person implements Comparable<Person>{
private Integer id;
private String userName;
private Integer age;
public Person() {
}
public Person(Integer id, String userName, Integer age) {
this.id = id;
this.userName = userName;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", userName='" + userName + '\'' +
", age=" + age +
'}';
}
// 先根据年龄进行降序排序
// 年龄相等的话按照姓名进行升序排序
@Override
public int compareTo(Person o) {
if (this.age.equals(o.age)){
return this.userName.compareTo(o.userName);
}else{
return -(this.age - o.age);
}
}
}
public class TreeMapTest {
public static void main(String[] args) {
Map<Person, String> map = new TreeMap<>();
map.put(new Person(1, "zhangsan", 15), "zhangsan");
map.put(new Person(2, "lisi", 18), "lisi");
map.put(new Person(3, "wangwu", 16), "wangwu");
map.put(new Person(4, "qianqi", 16), "qianxqi");
Set<Map.Entry<Person, String>> entries = map.entrySet();
for (Map.Entry entry : entries){
System.out.println(entry.getKey() + " --> " + entry.getValue());
}
}
}
控制台输出:

③实现Comparator接口进行排序
public class TreeMapTest {
public static void main(String[] args) {
Map<Person, String> map = new TreeMap<>(new Comparator<Person>() {
// 先根据年龄进行降序排序
// 年龄相等的话按照姓名进行升序排序
@Override
public int compare(Person o1, Person o2) {
if (o1.getAge().equals(o2.getAge())){
return o1.getUserName().compareTo(o2.getUserName());
}else{
return -(o1.getAge() - o2.getAge());
}
}
});
map.put(new Person(1, "zhangsan", 15), "zhangsan");
map.put(new Person(2, "lisi", 18), "lisi");
map.put(new Person(3, "wangwu", 16), "wangwu");
map.put(new Person(4, "qianqi", 16), "qianxqi");
Set<Map.Entry<Person, String>> entries = map.entrySet();
for (Map.Entry entry : entries){
System.out.println(entry.getKey() + " --> " + entry.getValue());
}
}
}
控制台输出:


浙公网安备 33010602011771号