在上篇文章当中我们主要介绍了java中集合的定义,分类以及各个类型集合对应的实现类。本篇文章我就来为大家梳理一下Java中各类型集合之间的区别。
(创作不易,请各位读者大大点赞评论关注😊)
一、关于集合的介绍
- 集合它是类似于数组一样的容器,但是比数组在操作数据上更加方便、灵活,可以使用一个集合存放各种类型的数据,在jdk 1.5 版本新增泛型的概念。用泛型来规范集合关于数据的储存。
- 集合在进行数据储存时能够实现自动扩容,还提供了快速进行数据增删改查的操作方法;
- 根据储存数据的方式将集合分为单列集合和双列集合:
- 单列集合最顶层的接口为 Collection ,双列集合最顶层的接口 Map;
- Collection 下有很多的子接口,最具有的代表性的接口 List 和 Set
- Map 下常用的实现类为 HashMap
二、单列集合顶层Collection和双列集合顶层Map的区别
注:(蓝色字体为精要总结,便于记忆)
Collection
是所有
单列集合最顶层的接口
,它继承了
Iterable
接口,证明
Collection
下面所有的
子接口和子接口实现类都
能够使用迭代器进行遍历
;
Collection
没有直接的实现类,所有的实现
都需要使用其子接口的实现类
;
Collection
无构造方法,不能直接创建对象,它创建对象的方式
需要使用多态
(父接口引用实现类对象) Collection c = new ArrayList();
1、遍历方法不同
Collection遍历集合的方法:
①.使用
迭代器 iterator()
进行遍历 (interator可能会出现迭代器使用异常,后续会继续讲解)
Iterator iterator = c.iterator();
while(iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
②.增强 for 循环遍历
for(Object obj:c){
System.out.println(obj);
}
Map
是双列集合最顶层的接口,没有像单列集合一样实现
Iterable
接口,因此
Map
集合
不支持迭代器进行遍历
;
①.
entrySet()
方法遍历
public void test_map(){
Map<String,String> map = new HashMap();
map.put("西游记","吴承恩");
map.put("红楼梦","曹雪芹");
map.put("三国演义","罗贯中");
map.put("水浒传","施耐庵");
Set<Map.Entry<String, String>> entries = map.entrySet();
for(Map.Entry<String, String> entry : entries){
System.out.println(entry.getKey() +"-----"+entry.getValue());
}
}
②.keySet() 方法
public void test_map(){
Map<String,String> map = new HashMap();
map.put("西游记","吴承恩");
map.put("红楼梦","曹雪芹");
map.put("三国演义","罗贯中");
map.put("水浒传","施耐庵");
Set<String> keys = map.keySet();
for(String key : keys){
System.out.println(key + "-----" + map.get(key));
}
}
2、数据存放要求不同
Collection下面的
List
集合且
允许元素重复储存且有序;
Collection下面的
Set
集合
是
元素无序、不重复集合
;常用的实现类是
HashSet
和
TreeSet
;其中
TreeSet
能够实现内部元素(
jdk
预定义类型)的排序,默认使用自然排序,还
可以自定义排序规则
;
HashSet
实现类不保证添加元素和遍历元素的顺序,
允许存在 null 元素,但只能有一个
(
注
:HashSet 去重原理需要牢牢掌握哦!
默认使用
Object
的
hashCode
方法和
equals
方法实现去重
)
treeset不能存储null。
Map
集合进行元素存放时,要求元素对象是一个
key = value
的一组键值对映射关系(
Entry
),其中
key
值不允许出现重复项,value
可以有重复的;
HashMap
是
Map
接口最常用的实现类,
允许出现 null 健和值,但是健只能有一个是 null,值可以有多个为 null。
Hashtable
不允许有 null 健和 null 值
3、底层原理和扩容机制不同
- ArrayList 底层是由数组实现的,所以它的特性为查找元素效率高,插入元素效率低;默认的初始容量为 10,在进行元素添加时会先判断容量是否足够,不足时系统会自动进行扩容;int newCapacity = oldCapacity + (oldCapacity >> 1); 即是容量变为原来的 1.5 倍;
- LinkedList因为底层由双向链表实现,因此它的插入效率高,查询效率低;
- HashSet 实现类 底层由数组+链表(可变红黑树)实现;通常使用空参构造进行对象的创建,创建的数组初始容量为 16,负载因子 0.75;当数组的储存数据达到 16*0.75 = 12 时,系统就会就行自动扩容,容量变为以前2倍;
-
TreeSet 底层有 TreeMap 实现,使用 排序树 (红黑树)的结构实现数据的储存,会对内部数据进行自然排序(升序);
-
HashMap 底层由 哈希表(数组+链表,链表可变为红黑树) 实现数据的储存; 数组的 初始容量为 16 , 负载因子 0.75 ,当储存的数据量达到 16*0.75 = 12 (阈值) 时,会自动进行数组的扩容, 扩容为原来的 2 倍;最大扩容到 64 ; 当容量变为 64 时,如果某个位置上链表长度达到 8,此时链表就会自动转换为红黑树 ;如果再对红黑树中的结点进行删除使结点个数少于 8 时,红黑树又会自动变为链表;
- LinkedHashMap 具备 HashMap 的一切功能,再此基础上还提供一个双向链表来维护元素添加的先后顺序,先添加的元素在链表的前面,后添加的在链表后面;
-
Hashtable 底层也是由 数组+链表 的方式实现数据储存,默认初始容 量为 11 ,负载因子 0.75 ;自动扩容的机制为: int newCapacity = ( oldCapacity << 1 ) + 1 ;
4、线程安全性不同
List集合中
ArrayList和LinkedList
都是线程不安全的,Vector是线程安全的;
Set集合
中的HashSet、TreeSet、LinkedHashSet都是线程不安全的。如果要用线程安全的set集合,可以使用java.util.concurrent包中提供的线程安全的set集合;
Map集合中HashMap、TreeMap是线程不安全的。Hashtable是线程安全的。
5、红黑树补充
红黑树是
二叉树
中一种特殊结构,具备一些特性:
性质
1.
结点是红色或黑色。
性质
2.
根结点是黑色
。
性质
3.
所有叶子都是黑色
。(叶子是
NIL
结点)
性质
4.
每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上
不能有两个连续的红色结点
)
性质
5.
从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点
性质
6.
红黑树能够实现自平衡,当左子树和右子树的深度差超过
1
时,红黑树会自动进行结点的旋转,让左
子树和右子树的深度差小于等于
1
,旋转后再改变结点的颜色;
性质
7
:红黑树能够自动进行结点元素的排序操作,每一个结点左边的结点值都比它小,每一个结点右边的结点
值都比它大