目录
一、类集
二、Collection接口
三、List接口
3.1 ArrayList
3.2 Vector
Vector与ArrayList的区别
3.3 linkedList(了解)
四、Set接口
4.1 HashSet(散列存放)
比较两个对象是否相同的方法(重点)
4.2 TreeSet(排序的子类)
五、集合的输出(遍历)
5.1 Iterator(重点)
扩展:迭代器的快速失败和安全失败
5.2 ListIterator
5.3 Enumeration(废弃的接口,了解)
5.4 foreach
六、Map接口
6.1 HashMap
扩展:哈希表和HashMap源码分析
6.2 Hashtable
扩展:HashMap与Hashtable的区别
扩展:ConcurrentHashMap
6.3 TreeMap
6.4 Map集合的输出
扩展:TreeMap和linkedHashMap的区别
扩展:Map集合存储自定义对象
一、类集
- 类集是java对数据结构成熟的实现,在jdk1.2之后正式引入。所有类集操作的接口或类都在java.util包中
- java类集结构图如下图所示
- 类集中最大的几个操作接口:Collection、Map、Iterator
二、Collection接口
- Collection接口是在整个java类集中保存单值的最大操作父接口,里面每次操作时都只能保存一个对象的数据,此接口定义在java.util包中
- 接口定义如下
public interface Collection extends Iterable
- 此接口的常用方法
- Collection接口的全部子类或子接口将全部继承以上方法,在开发过程中通常使用的是其子接口:List、Set
三、List接口
- List是Collection的子接口,也是单值存储的集合,里面的所有内容都是允许重复的
- List子接口的定义
public interface List extends Collection
- List接口的扩充方法
- List接口的常用实现类
- ArrayList(95%)
- Vector(4%)
- linkedList(1%)
3.1 ArrayList
- ArrayList是List接口的子类,定义如下
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, Serializable
- 顺序存储,存储的内容是有序的
- 因为底层是用数组实现的
- ArrayList集合对元素的增删慢,查找快,日常开发中常用于数据查询、遍历数据
- 简单的源码解析
- 在调用无参构造器的时候,构造器仅仅是构造了一个空的数组,容量为0
- 也可以通过有参构造器指定集合的初始容量
- 在第一次调用add方法时会初始化一个容量为10的空数组
- add方法中会判断当前数组长度,与数组存储元素的个数是否相同,相同说明数组满了,需要进行扩容操作
- 扩容操作就是对数组的长度进行1.5倍扩容,其中还有一些其他的判断细节,如判断是否超过int的最大范围、是否为空数组等
- 具体的源码分析见下面网址
ArrayList源码分析_future_god_qr的博客-CSDN博客
3.2 Vector
- Vector也是List接口的子类,此类定义如下
public class Vector extends AbstractList
implements List, RandomAccess, Cloneable, Serializable
Vector与ArrayList的区别
- 因为底层是用数组实现的
- 在调用无参构造器的时候,构造器仅仅是构造了一个空的数组,容量为0
- 也可以通过有参构造器指定集合的初始容量
- 在第一次调用add方法时会初始化一个容量为10的空数组
- add方法中会判断当前数组长度,与数组存储元素的个数是否相同,相同说明数组满了,需要进行扩容操作
- 扩容操作就是对数组的长度进行1.5倍扩容,其中还有一些其他的判断细节,如判断是否超过int的最大范围、是否为空数组等
- 具体的源码分析见下面网址
ArrayList源码分析_future_god_qr的博客-CSDN博客
- Vector也是List接口的子类,此类定义如下
public class Vectorextends AbstractList implements List , RandomAccess, Cloneable, Serializable
Vector与ArrayList的区别
| 区别点 | ArrayList | Vector |
| 时间 | 是新的类,在jdk1.2之后推出 | 是旧的类,在jdk1.0时就有 |
| 性能 | 性能较高,采用了异步处理,线程不安全 | 性能较低,采用了同步处理,线程安全 |
| 输出 | 支持Iterator、ListIterator输出 | 除了支持Iterator、ListIterator输出,还支持Enumeration输出 |
3.3 linkedList(了解)
- 此类用的非常少,定义如下
public class linkedList extends AbstractSequentialList
implements List, Deque, Cloneable, Serializable
- 底层使用的是双向链表结构,增删快,查找慢
- 可以把linkedList当成栈或队列结构使用,开发时用的比较少,了解即可
- 此类继承了AbstractList,所以是List的子类;但此类也是Queue接口的子类,Queue接口定义了如下方法
- 可以把linkedList当成栈或队列结构使用,开发时用的比较少,了解即可
四、Set接口
- Set接口也是Collection的子接口,与List接口最大的不同在于,Set接口里面的内容是不允许重复的
- Set接口没有对Collection接口进行扩充,因此没有List接口中定义的get(int index)方法,所以Set无法通过循环获取数据
- 但是可以通过迭代器获取数据
- 或者使用toArray方法变成数组后再遍历获取数据,toArray方法最好指定泛型,因为Set在操作的时候已经指定了数据类型
- Set接口的常用子类有两个:HashSet、TreeSet
4.1 HashSet(散列存放)
- 因为Set接口没有扩充任何Collection接口中的方法,所以方法都是Collection中定义的
- HashSet属于散列的存放类集,里面的内容都是无序存放的
- 底层是使用HashMap实现的,即使用了HashMap的key来实现HashSet,后面讲解map的时候会阐述
- 底层存放数据的数据结构是哈希表(散列表)
- jdk1.8后哈希表底层采用数组+链表+红黑树实现
比较两个对象是否相同的方法(重点)
- 有两种方法
- 第一种:判断两个对象的编码是否一致,这个方法需要通过hashCode()完成,即:每个对象有唯一的编码
- 第二种:如果还需要进一步验证对象中的每个属性是否相等,还需要通过equals完成。所以此时需要重写Object类中的hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的
- 即equals和hashCode方法都重写
- 举例
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person per = (Person) obj;
if (per.name.equals(this.name) && per.age == this.age) {
return true;
} else {
return false;
}
}
public int hashCode() {
return this.name.hashCode() * this.age;
}
- 写这个知识点,是因为后面的TreeSet判断对象是否相同,是通过Comparable或者Comparator间接完成的,在HashSet中添加实现Comparable的对象,仍然存在我们认为的相同对象(属性值都相同)依然可以添加进set集合的情况
- 这种情况下,需要真正实现重复元素的判断,需要重写equals和hashCode方法
4.2 TreeSet(排序的子类)
- 与HashSet不同的是,TreeSet本身属于排序的子类,此类定义如下
public class TreeSet extends AbstractSet
implements NavigableSet, Cloneable, Serializable
- 底层存储数据的数据结构是二叉树,因此结果是有序的
- 有序指的是按一定规则排列后的顺序,而不是输入的顺序
- 底层是基于TreeMap实现的
- TreeSet存储的数据是需要有比较规则的,有两个方式实现
- 方式一:传入TreeSet的数据实现Comparable接口,实现compareTo方法(判断大小的逻辑方法)
- 升序排列
- this小返回负数,一样大返回0,this大返回正数
- 降序排列
- this小返回正数,一样大返回0,this大返回负数
- 返回0说明是同一个对象,无法重复添加进set集合
- 方式二:TreeSet传入一个比较器Comparator,在比较器里面重写compare方法
- 该方法的判断逻辑和方式一一致,只不过this变成了传入的第一个参数o1,被比较的是第二个参数o2
- 可以使用匿名内部类的方式实现Comparator,再重写compare方法
五、集合的输出(遍历)
- 对于集合的输出(遍历),有以下几种方式
- Iterator迭代输出(90%)
- 用于List和Set,不能用于Map
- 迭代效率最高
- ListIterator(5%)
- 只能用于迭代List
- Enumeration(1%)
- foreach(4%)
- 迭代集合时本质还是使用Iterator
5.1 Iterator(重点)
- Iterator属于迭代输出
- 原理
- 不停判断是否有下一个元素,有的话就输出
- 接口定义
public interface Iterator
- 要想使用Iterator接口,则必须使用Collection接口。在Collection接口中规定了一个iterator()方法,可以用于为Iterator接口进行实例化操作
- 接口定义的方法
- 但是可以通过迭代器获取数据
- 或者使用toArray方法变成数组后再遍历获取数据,toArray方法最好指定泛型,因为Set在操作的时候已经指定了数据类型
- 因为Set接口没有扩充任何Collection接口中的方法,所以方法都是Collection中定义的
- HashSet属于散列的存放类集,里面的内容都是无序存放的
- 底层是使用HashMap实现的,即使用了HashMap的key来实现HashSet,后面讲解map的时候会阐述
- 底层存放数据的数据结构是哈希表(散列表)
- jdk1.8后哈希表底层采用数组+链表+红黑树实现
- 底层存放数据的数据结构是哈希表(散列表)
比较两个对象是否相同的方法(重点)
- 有两种方法
- 第一种:判断两个对象的编码是否一致,这个方法需要通过hashCode()完成,即:每个对象有唯一的编码
- 第二种:如果还需要进一步验证对象中的每个属性是否相等,还需要通过equals完成。所以此时需要重写Object类中的hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的
- 即equals和hashCode方法都重写
- 举例
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Person)) {
return false;
}
Person per = (Person) obj;
if (per.name.equals(this.name) && per.age == this.age) {
return true;
} else {
return false;
}
}
public int hashCode() {
return this.name.hashCode() * this.age;
}
- 写这个知识点,是因为后面的TreeSet判断对象是否相同,是通过Comparable或者Comparator间接完成的,在HashSet中添加实现Comparable的对象,仍然存在我们认为的相同对象(属性值都相同)依然可以添加进set集合的情况
- 这种情况下,需要真正实现重复元素的判断,需要重写equals和hashCode方法
4.2 TreeSet(排序的子类)
- 与HashSet不同的是,TreeSet本身属于排序的子类,此类定义如下
public class TreeSet extends AbstractSet
implements NavigableSet, Cloneable, Serializable
- 底层存储数据的数据结构是二叉树,因此结果是有序的
- 有序指的是按一定规则排列后的顺序,而不是输入的顺序
- 底层是基于TreeMap实现的
- TreeSet存储的数据是需要有比较规则的,有两个方式实现
- 方式一:传入TreeSet的数据实现Comparable接口,实现compareTo方法(判断大小的逻辑方法)
- 升序排列
- this小返回负数,一样大返回0,this大返回正数
- 降序排列
- this小返回正数,一样大返回0,this大返回负数
- 返回0说明是同一个对象,无法重复添加进set集合
- 方式二:TreeSet传入一个比较器Comparator,在比较器里面重写compare方法
- 该方法的判断逻辑和方式一一致,只不过this变成了传入的第一个参数o1,被比较的是第二个参数o2
- 可以使用匿名内部类的方式实现Comparator,再重写compare方法
五、集合的输出(遍历)
- 对于集合的输出(遍历),有以下几种方式
- Iterator迭代输出(90%)
- 用于List和Set,不能用于Map
- 迭代效率最高
- ListIterator(5%)
- 只能用于迭代List
- Enumeration(1%)
- foreach(4%)
- 迭代集合时本质还是使用Iterator
5.1 Iterator(重点)
- Iterator属于迭代输出
- 原理
- 不停判断是否有下一个元素,有的话就输出
- 接口定义
public interface Iterator
- 要想使用Iterator接口,则必须使用Collection接口。在Collection接口中规定了一个iterator()方法,可以用于为Iterator接口进行实例化操作
- 接口定义的方法
- 第一种:判断两个对象的编码是否一致,这个方法需要通过hashCode()完成,即:每个对象有唯一的编码
- 第二种:如果还需要进一步验证对象中的每个属性是否相等,还需要通过equals完成。所以此时需要重写Object类中的hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的
- 即equals和hashCode方法都重写
- 这种情况下,需要真正实现重复元素的判断,需要重写equals和hashCode方法
- 与HashSet不同的是,TreeSet本身属于排序的子类,此类定义如下
public class TreeSetextends AbstractSet implements NavigableSet , Cloneable, Serializable
- 底层存储数据的数据结构是二叉树,因此结果是有序的
- 有序指的是按一定规则排列后的顺序,而不是输入的顺序
- 底层是基于TreeMap实现的
- TreeSet存储的数据是需要有比较规则的,有两个方式实现
- 方式一:传入TreeSet的数据实现Comparable接口,实现compareTo方法(判断大小的逻辑方法)
- 升序排列
- this小返回负数,一样大返回0,this大返回正数
- 降序排列
- this小返回正数,一样大返回0,this大返回负数
- 返回0说明是同一个对象,无法重复添加进set集合
- 升序排列
- 方式二:TreeSet传入一个比较器Comparator,在比较器里面重写compare方法
- 该方法的判断逻辑和方式一一致,只不过this变成了传入的第一个参数o1,被比较的是第二个参数o2
- 可以使用匿名内部类的方式实现Comparator,再重写compare方法
- 方式一:传入TreeSet的数据实现Comparable接口,实现compareTo方法(判断大小的逻辑方法)
五、集合的输出(遍历)
- 对于集合的输出(遍历),有以下几种方式
- Iterator迭代输出(90%)
- 用于List和Set,不能用于Map
- 迭代效率最高
- ListIterator(5%)
- 只能用于迭代List
- Enumeration(1%)
- foreach(4%)
- 迭代集合时本质还是使用Iterator
5.1 Iterator(重点)
- Iterator属于迭代输出
- 原理
- 不停判断是否有下一个元素,有的话就输出
- 接口定义
public interface Iterator
- 要想使用Iterator接口,则必须使用Collection接口。在Collection接口中规定了一个iterator()方法,可以用于为Iterator接口进行实例化操作
- 接口定义的方法
- Iterator迭代输出(90%)
- 用于List和Set,不能用于Map
- 迭代效率最高
- ListIterator(5%)
- 只能用于迭代List
- Enumeration(1%)
- foreach(4%)
- 迭代集合时本质还是使用Iterator
- Iterator属于迭代输出
- 原理
- 不停判断是否有下一个元素,有的话就输出
- 接口定义
public interface Iterator
- 要想使用Iterator接口,则必须使用Collection接口。在Collection接口中规定了一个iterator()方法,可以用于为Iterator接口进行实例化操作
- 接口定义的方法
- Iterator中的操作指针在第一条指针之上,当调用next()方法时,获取当前指针指向的值并向下移动,使用hasNext()可以检查序列中是否还有元素
- 在进行迭代输出的时候,如果想要删除当前元素,只能使用Iterator接口中的remove方法,不能使用集合中的remove方法,否则将出现未知的错误。
- 集合中很少有删除元素的操作,所以可以忽略这一点
- 此接口只能进行从前到后的单向输出,如果要进行双向输出,必须使用其子接口ListIterator
- 举例
package org.listdemo.iteratordemo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo02 {
public static void main(String[] args) {
Collection all = new ArrayList();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Iterator iter = all.iterator();
while (iter.hasNext()) {// 判断是否有下一个元素
String str = iter.next(); // 取出当前元素
if (str.equals("C")) {
all.remove(str); // 错误的, 调用了集合中的删除
} else {
System.out.print(str + "、 ");
}
}
}
}
扩展:迭代器的快速失败和安全失败
- 集合在进行迭代的时候都存在迭代器的快速失败和安全失败
- 失败的含义
- 遍历过程中数据发生了改变,最后遍历的结果和最初的预期不一定是一样的,可能会发生错误,这就叫失败
- 快速失败
- 迭代器遍历的是集合本身,迭代的时候会有一个记录自己遍历到哪一个数据的变量。如果迭代器在遍历的时候,有另外一条执行路径对集合内容进行了改变(比如删了几个元素,造成元素个数的改变),这个时候迭代器遍历到后面被操作的数据时可能会出错(比如原本十个数据,遍历的过程删到只剩五个数据,遍历的时候可能就会出问题)
- 这个时候迭代器就会抛出ConcurrentModificationException异常
- 简单来说,就是遍历的过程中集合发生了改变,就会发生快速失败
- 安全失败
- 安全失败指的是发生失败情况的时候,遍历的数据不会出错
- 安全失败是在遍历集合之前,将集合复制了一份,遍历的是复制的集合
- 此时遍历结果与原集合是否被操作无关
- 一般情况下集合都是安全失败的,具体要看API文档的描述
5.2 ListIterator
- ListIterator是可以进行双向输出的迭代接口,是Iterator的子接口,定义如下
public interface ListIterator
extends Iterator
- 此迭代器只能用于List集合的遍历,不能用于Set
- 此接口扩展了以下操作方法
- 失败的含义
- 遍历过程中数据发生了改变,最后遍历的结果和最初的预期不一定是一样的,可能会发生错误,这就叫失败
- 迭代器遍历的是集合本身,迭代的时候会有一个记录自己遍历到哪一个数据的变量。如果迭代器在遍历的时候,有另外一条执行路径对集合内容进行了改变(比如删了几个元素,造成元素个数的改变),这个时候迭代器遍历到后面被操作的数据时可能会出错(比如原本十个数据,遍历的过程删到只剩五个数据,遍历的时候可能就会出问题)
- 这个时候迭代器就会抛出ConcurrentModificationException异常
- 简单来说,就是遍历的过程中集合发生了改变,就会发生快速失败
- 安全失败指的是发生失败情况的时候,遍历的数据不会出错
- 安全失败是在遍历集合之前,将集合复制了一份,遍历的是复制的集合
- 此时遍历结果与原集合是否被操作无关
- 安全失败是在遍历集合之前,将集合复制了一份,遍历的是复制的集合
- 一般情况下集合都是安全失败的,具体要看API文档的描述
- ListIterator是可以进行双向输出的迭代接口,是Iterator的子接口,定义如下
public interface ListIteratorextends Iterator
- 此迭代器只能用于List集合的遍历,不能用于Set
- 此接口扩展了以下操作方法
5.3 Enumeration(废弃的接口,了解)
- Enumeration 是一个非常古老的输出接口, 其也是一个元老级的输出接口, 最早的动态数组使用 Vector 完成, 那么只要是使用了 Vector 则就必须使用 Enumeration 进行输出。
5.4 foreach
- foreach可以用来输出数组的内容,也可以输出集合中的内容
- 集合只能是Collection,即List和Set
- 在使用foreach时需要注意,里面的操作泛型要指定具体的类型,这样在输出的时候才会更加有针对性
- foreach又叫增强for循环,在迭代集合的时候本质还是使用迭代器来进行迭代
- 语法格式
for(数据类型 变量 : 集合或数组名){
操作
}
六、Map接口
- Map是双值存储的集合,集合中存储的是一个个键值对数据(将键映射到值的对象)
- 与Collection没有关系,是第二大的集合操作接口
- Map集合不能包含重复的键(key),每个键最多可以映射一个值(value),值可以重复
- Map本身是一个接口,常用的子类有:HashMap、TreeMap、Hashtable
- 常用API
- 注意map转为set和Collection的方法
- foreach可以用来输出数组的内容,也可以输出集合中的内容
- 集合只能是Collection,即List和Set
- 在使用foreach时需要注意,里面的操作泛型要指定具体的类型,这样在输出的时候才会更加有针对性
- foreach又叫增强for循环,在迭代集合的时候本质还是使用迭代器来进行迭代
- 语法格式
for(数据类型 变量 : 集合或数组名){
操作
}
六、Map接口
- Map是双值存储的集合,集合中存储的是一个个键值对数据(将键映射到值的对象)
- 与Collection没有关系,是第二大的集合操作接口
- Map集合不能包含重复的键(key),每个键最多可以映射一个值(value),值可以重复
- Map本身是一个接口,常用的子类有:HashMap、TreeMap、Hashtable
- 常用API
- 注意map转为set和Collection的方法
- 与Collection没有关系,是第二大的集合操作接口
- 注意map转为set和Collection的方法
6.1 HashMap
- HashMap是Map的子类,定义如下
public class HashMap extends AbstractMap
implements Map, Cloneable, Serializable
- HashMap本身是属于无序存放的
- 底层数据结构是哈希表
- key也可以是null,但是只能有一个null
扩展:哈希表和HashMap源码分析
HashMap源码分析_future_god_qr的博客-CSDN博客
6.2 Hashtable
- Hashtable是最早的key-value的操作类,在jdk1.0的时候推出,基本操作与HashMap类似。
- Hashtable中不可以插入null值
扩展:HashMap与Hashtable的区别
HashMap源码分析_future_god_qr的博客-CSDN博客
6.2 Hashtable
- Hashtable是最早的key-value的操作类,在jdk1.0的时候推出,基本操作与HashMap类似。
- Hashtable中不可以插入null值
扩展:HashMap与Hashtable的区别
| 区别 | HashMap | Hashtable |
| 推出时间 | jdk1.2之后推出的新的操作类 | jdk1.0时推出的旧的操作类 |
| 性能 | 异步处理,性能较高,线程不安全 | 同步处理,性能较低,线程安全 |
| 能否设置null | 允许设置为null | 不允许设置,否则将出现空指针异常 |
扩展:ConcurrentHashMap
- jdk1.7时推出的一种数组+分段锁实现的map集合
- 采用分段锁机制,保证了线程安全,效率比较高(没有HashMap高)
- 操作同一个桶的时候才需要排队,操作的如果不是同一个桶,则不需要排队
6.3 TreeMap
- TreeMap子类是允许key进行排序的操作子类,其本身在操作的时候将按照key进行排序,另外,key中的内容可以为任意对象,但是要求对象所在的类必须实现Comparable接口
- 在使用Map接口的时候并不关心其是否排序,所以此类只需要知道其特点即可
6.4 Map集合的输出
- Map接口没有iterator()方法的定义,所以Map接口本身是不能直接使用Iterator进行输出的
- 如果非要使用Iterator对Map集合进行输出,可以采取以下步骤
- 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
- 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
- 之后使用 Iterator 接口进行迭代输出, 每一次的迭代都可以取得一个 Map.Entry 的实例
- 通过 Map.Entry 进行 key 和 value 的分离
- Map.Entry 本身是一个接口。 此接口是定义在 Map 接口内部的, 是 Map 的内部接口。 此内部接口使用 static 进行定义,所以此接口将成为外部接口
- 实际上来讲, 对于每一个存放到 Map 集合中的 key 和 value 都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了Map 集合之中
- Map.Entry接口的常用方法
- 采用分段锁机制,保证了线程安全,效率比较高(没有HashMap高)
- TreeMap子类是允许key进行排序的操作子类,其本身在操作的时候将按照key进行排序,另外,key中的内容可以为任意对象,但是要求对象所在的类必须实现Comparable接口
- 在使用Map接口的时候并不关心其是否排序,所以此类只需要知道其特点即可
6.4 Map集合的输出
- Map接口没有iterator()方法的定义,所以Map接口本身是不能直接使用Iterator进行输出的
- 如果非要使用Iterator对Map集合进行输出,可以采取以下步骤
- 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
- 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
- 之后使用 Iterator 接口进行迭代输出, 每一次的迭代都可以取得一个 Map.Entry 的实例
- 通过 Map.Entry 进行 key 和 value 的分离
- Map.Entry 本身是一个接口。 此接口是定义在 Map 接口内部的, 是 Map 的内部接口。 此内部接口使用 static 进行定义,所以此接口将成为外部接口
- 实际上来讲, 对于每一个存放到 Map 集合中的 key 和 value 都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了Map 集合之中
- Map.Entry接口的常用方法
- 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
- 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
- 之后使用 Iterator 接口进行迭代输出, 每一次的迭代都可以取得一个 Map.Entry 的实例
- 通过 Map.Entry 进行 key 和 value 的分离
- 实际上来讲, 对于每一个存放到 Map 集合中的 key 和 value 都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了Map 集合之中
- 此外,还可以使用foreach+lambda表达式实现Map集合的输出
扩展:TreeMap和linkedHashMap的区别
- TreeMap
- 使用平衡二叉树存储
- 存储的数据是有序的
- 不是按存储的顺序,而是按key实现的的Comparable接口中定义的排序规则
- linkedHashMap
- 额外使用了一个双向链表存储数据
- 存储的数据既在哈希表中,又在链表中
- 链表维护存储的顺序,按存存储的顺序排序
扩展:Map集合存储自定义对象
- 自定义的对象一定要支持equals和hashCode方法
- 如果没重写这两个方法,默认使用的是Object的方法,性能不高
- 如果自定义的对象作为Map的key值存储,存储之后不能随便修改对象的属性值
- 如果需要改变对象的内容,就不要存储在key的位置
- 一旦改变了key的对象的值,很容易造成hash值错乱的问题
- 使用平衡二叉树存储
- 存储的数据是有序的
- 不是按存储的顺序,而是按key实现的的Comparable接口中定义的排序规则
- 额外使用了一个双向链表存储数据
- 存储的数据既在哈希表中,又在链表中
- 链表维护存储的顺序,按存存储的顺序排序
- 存储的数据既在哈希表中,又在链表中
- 自定义的对象一定要支持equals和hashCode方法
- 如果没重写这两个方法,默认使用的是Object的方法,性能不高
- 如果自定义的对象作为Map的key值存储,存储之后不能随便修改对象的属性值
- 如果需要改变对象的内容,就不要存储在key的位置
- 一旦改变了key的对象的值,很容易造成hash值错乱的问题



