关于容器类的简单学习,后续还会修改~
首先插入一张图
有图可见java集合框架主要分为collection 和Map两大接口及其子类
collection
list
list是一个元素有序,可以重复(list的数据结构就是一个序列,新建时直接在内存中开辟一块连续的内存,将空间地址与索引对应,存储时彼此不会有影响),并且可以为null的集合。
list的equals方法默认和==一致,比较的时地址,所以需要从写放入list中的类的 equals和hashCode方法
List.subList
方法并没有像我们想的那样:创建一个新的 List,而是与原来的list公用一个引用,只是改了 offset 和 size,可有以下列子论证:
list与array
相似之处: 都可以表示一组同类型的对象 ;都使用下标进行索引
不同之处: 数组可以存任何类型元素;list 不可以存基本数据类型,必须要包装数组容量固定不可改变;List 容量可动态增长数组效率高; List 由于要维护额外内容,效率相对低一些
arraylist
本质是一个object类型的数组所以元素可以为null,
只是可以扩容(初始值为10),当size<6时直接增加到12否则变为1.5倍
由于arraylist不是线程安全所以并发时会引发迭代器的快速失败,因此需要在并发时加锁,
List list = Collections.synchronizedList(new ArrayList(...));
linkedlist
可以实现栈,队列
本质是一个链表,不存在容量限制
相对于arraylist更非空间,因为要维护链表结构
元素是有序的,输出顺序与输入顺序一致
允许元素为 null
所有指定位置的操作都是从头开始遍历进行的
和 ArrayList 一样,不是同步容器
linkedList与arrayList主要从以下四点比较
1、查找速度 2、修改速度 3、容量 4、内存开销
Vector
线程安全类型的ArrayList(其实还是有差别的)
Vector 特点
- 底层由一个可以增长的数组组成
- Vector 通过 capacity (容量) 和 capacityIncrement (增长数量) 来尽量少的占用空间
- 扩容时默认扩大两倍
- 最好在插入大量元素前增加 vector 容量,那样可以减少重新申请内存的次数
- 通过 iterator 和 lastIterator 获得的迭代器是 fail-fast 的
- 通过 elements 获得的老版迭代器 Enumeration 不是 fail-fast 的
- 同步类,每个方法前都有同步锁 synchronized
- 在 JDK 2.0 以后,经过优化,Vector 也加入了 Java 集合框架大家族
与ArrayList比较
共同点:
- 都是基于数组
- 都支持随机访问
- 默认容量都是 10
- 都有扩容机制
区别:
- Vector 比 ArrayList 多一种迭代器 Enumeration
- Vector 是线程安全的,ArrayList 不是
- Vector 默认扩容 2 倍,ArrayList 是 1.5
如果没有线程安全的需求,一般推荐使用 ArrayList,而不是 Vector,因为每次都要获取锁,效率太低。
set
基于map实现的
hashSet
无序块
treeSet
有序,效率相对较低
linkedHashSet
结合前两者,有序,效率只比hashset差点
queue
队列:尾部添加,头部删除,类似于生活中的排队。
单队列:会出现假溢出
循环队列:
queue:用来存放 等待处理元素 的集合,这种场景一般用于缓冲、并发访问。除了继承 Collection 接口的一些方法,Queue 还添加了额外的 添加、删除、查询操作(实现队列的方法)。
Map(与collection同一等级)
其实就是键值对 (key-value) 的映射
keyset:是map中键的集合,是一个set集合所以不能重复
values:是map中值得集合,是collection所以可以重复,(但是set也是collection,所以有点不解,猜测应该是指list)
entry:表示一组映射,map中的entry是一个set的集合。
由map的结构组成便有三个遍历的方法:
由key间接遍历值
Set<T> strings = m.keySet();
Iterator<String> iterator = strings.iterator();
while(iterator.hasNext()){
System.out.println(m.get(iterator.next()));
}
由value直接遍历得到值
Collection<T> values = m.values();
Iterator<T> iterator = values.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
由entry得到键值对集合,遍历该集合得到值
Set<Map.Entry<T, T>> entries = m.entrySet();
for (Map.Entry<T, T> entry : entries) {
System.out.println(entry.getValue());
}
HashTable
古老线程安全
HashMap(默认大小为16,加载因子为0.75)
- 底层实现是 链表数组(Node<K,V>[] table),JDK 8 后又加了 红黑树
- 实现了 Map 全部的方法
- key 用 Set 存放,所以想做到 key 不允许重复,key 对应的类需要重写 hashCode 和 equals 方法
- 允许空键和空值,放在第一(计算hash值时直接返回0)
- 元素是无序的,而且顺序会不定时改变
- 插入、获取的时间复杂度基本是 O(1)(前提是有适当的哈希函数,让元素分布在均匀的位置)
- 遍历整个 Map 需要的时间与 桶(数组) 的长度成正比(因此初始化时 HashMap 的容量不宜太大)
- 两个关键因子:初始容量、加载因子
Hashtable除了不能为null以及是线程同步几乎和hashmap一样
TreeMap
有序(基于元素的固有顺序 ,由 Comparator 或者 Comparable 确定),效率较低
LinkedHashMap
结合了hashmap与treemap 有序(基于元素进入集合的顺序或者被访问的先后顺序排序)并且效率也不错只是比hashmap慢一点