Java集合详解
集合大纲
单列集合
双列集合
Collection接口方法
Collection接口实现类的特点
- Collection实现子类可以存放多个元素,每个元素都可以是Object
- 有些Collection的实现类,可以存放重复的元素(List),有些不可以(Set)
- 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
Collection接口的常用方法
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素,类似于指针,和我们之前用的结果集很像。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即返回一个迭代器。
- Iterator的结构是图
- Iterator仅用于遍历集合,Iterator本身并不存放对象。
迭代器遍历
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(4);
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
for循环增强
增强for循环,可以代替Iterator迭代器,特点:增强for循环就是简化版的iterator,本质一样,只能用于遍历集合或数组。
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(4);
for (Object obj : list){
System.out.println(obj);
}
}
}
List接口和常用方法
- List集合类中元素是有序的、且可以重复
- List集合类的每个元素都有其对应的顺序索引
- List容器中的元素都有对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
- 常用的接口有ArrayList、LinkedList和Vector
ArrayList底层结构
- ArrayList中维护了一个Object类型的数组elementData
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始数组容量为0,第一次添加,则扩容为10,如果要再次扩容,则扩容为1.5倍。
- 如果使用的是指定大小的构造器,则初始数组容量为指定大小,如果需要扩容,则直接扩容为1.5倍
Vector底层结构
- Vector底层也是一个对象数组
- Vector是线程同步的,Vector类的操作方法带有synchronize
- 在开发中、需要线程同步安全是,考虑使用Vector
- Vector在扩容时,是2倍,而非是ArrayList的1.5倍
LinkedList底层结构
- LinkedList实现了双向链表和双端队列特点
- 可以添加任意元素(元素可重复),包括null
- 线程不安全,没有实现同步
ArrayList、Vector、LinkedList的区别
接口 | 底层结构 | 线程安全 | 效率 | 扩容机制 |
---|---|---|---|---|
ArrayList | 可变数组 | 线程不安全 | 增删慢、查找快 | 第一次10,满后扩容为1.5倍 |
Vector | 可变数组 | 线程安全 | 效率不高 | 第一次是10,满后扩容为2倍 |
LinkedList | 双向链表 | 线程不安全 | 增删快、查找慢 | 双向链表没有固定的容量,添加直接在链表尾部加上节点即可 |
Set接口和常用方法
- 无序,没有索引
- 不允许重复元素,所以最多包含一个null
- 实现的接口有:HashSet、TreeSet
HashSet接口
- HashSet的底层是HashMap、HashMap的底层是数组+链表+红黑树
- 第一次添加时,table数组扩容到16,临界值是16*加载因此0.75=12,达到12是,就扩容2倍。
- 可以存放null值,但是只能有一个null
- HashSet不保证元素是有序的,取决与hash后,再确定索引的结果
- 不能有重复元素/对象。
HashSet元素不重复的原因
HashSet底层是HashMap。当添加一个元素是,先得到hash值,hash值会转成索引值,找到存储数据表table,看这个索引位置是否已经存放的有元素。如果没有(没有发生哈希碰撞),则直接加入。如果有(发生了哈希碰撞)、则用equals()方法判断在这个hash值对应的索引的链表上是否有与之相等的节点,如果有则不添加,如果没有则添加到这个链表的结尾。
在Java8中,如果一条链表的元素个数达到了8,并且table的大小不小于64,就会进行树化(红黑树)
LinkedHashSet
- 底层是LinkedHashMap,维护了一个hash表和双向链表
- 每一个节点有before和after属性,这样可以形成双向链表
- 在添加一个元素时,先求hash值,再求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表,如果已经存在,则不添加(和HashSet类似)
- 我们遍历LinkedHashSet也能确保插入顺序和遍历顺序一致
Map接口和常用方法
- Map与Collection并列存在。用于保存具有映射关系的数据
- Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不允许重复,原因和HashSet一样,而value是可以重复的
- Map的key可以为null,value也可以为null,key只能有一个null,而value为null可以多个。
- 常用String类作为Map的key
- key和value之间存在单向一对一关系,即,通过指定的key总能找到对应的value
HashMap
- HashMap是Map接口使用频率最高的实现类
- Has和Map是以key-value对的方式来存储数据
- key不能重复,但是值可以重复,允许使用null键和null值
- 添加相同的key,则会覆盖原来的key-value,等同于修改
- 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的
- 没有实现同步,因此是线程不安全的
HashTable接口
- 存放的元素是键值对
- 键值都不能为null,否则会抛出NullPointerException
- 适用方法和HaspMap一致
- hashTable是线程安全的