集合框架库
1. 集合简介
1.1 集合概念
什么是集合:存储数据的容器。 数组、数据库也可以存储数据。
1.2 集合的作用
- 集合的作用:存储数据。
- 集合的使用场景:对元素数量没有明确判断,并且容易发生改变,存储好的数据有很多复杂的后续操作。
1.3 集合和其他容器的比较
- 数据库:mysql文件类型的数据库。数据可以长时间存储,从而可以使数据反复的使用。
- 数组 / 集合:暂存,单次使用。项目运行起来:创建数组或者集合,将数据存进去。一旦项目结束数组和集合都不存在,数据也随即不存在。
- 数组的缺点:(1)扩容操作可能在代码中要出现多次。(2)数组越界。
Integer[] arr = new Integer[10];
//异常:数组越界
//下标:index
int get(int index, Integer[] arr){
if(index > 0 && index < arr.length)
return arr[index];
}
-
集合:对普通数据结构的封装。
数组:ArrayList、Vector对数组封装,会自动扩容,自动判断下标是否越界。 链表:LinkedList 堆:优先级队列
-
数组和集合相比:
1.数组是静态的,集合是动态的。 2.集合功能更全面(功能之一:下标是否越界) 3.数组需要指定类型,存储的数据必须是该类型。 集合创建时不需要(可有可无)。如果有,存储数据必须是该类型;如果没有,存储数据可以是任意的。 注意:集合指定存储数据类型的方式用的较多,原因是更好管理 4.数组在指定类型时,既可以使用普通类型,也可以使用引用类型。 集合只能使用引用类型。 5.数组是Java内置类型,数组效率更高。 什么时候用集合,什么时候用数组? 数组:对元素数量有明确的判断,并且轻易不会发生改变,存储好的数据不会有太多复杂后续操作。 集合与之相反。
2. 集合框架库概述
集合框架主要结构图:
3大部分:Iterator、Collection、Map
(面试)集合框架中的顶级接口有哪些:Collection、Map
各个接口的特点:
-
Iterator:迭代器遍历集合,从前向后遍历。
ListIterator接口:是Iterator接口的子接口,可以进行双向输出,既可以从前向后遍历,也可以从后向前。
-
Collection接口:是存放一组单值的最大接口。
所谓的单值是指集合中的每个元素都是一个对象。一般很少直接使用此接口直接操作。(1)List接口:是Collection接口的子接口,也是最常用的接口。此接口对Collection接口进行了大量的扩充,里面的内容是允许重复允许为NULL的并且有序(插入的顺序)。 ArrayList :【 22】 【 11】 【15】【null】【15】 (2)Set接口:是Collection接口的子类,没有对Collection接口进行扩充,里面不允许存放重复内容! SortedSet接口:单值的排序接口。实现此接口的集合类,里面的内容可以使用比较器排序。 TreeSet 【 11】 【 15】 【22】 (3)Queue接口:是Collection接口的子接口,队列接口,具有队列先入先出的特点。此接口的子类可以实现队列操作。
-
Map接口:是存放一对值的最大接口!即接口中的每个元素都是一对,以key --> value的形式保存,并且Key是不重复的,元素的存储位置由key决定。也就是可以通过key去寻找key-value的位置,从而得到value的值。适合做查找工作。
(1)SortedMap接口:存放一对值的排序接口。实现此接口的集合类,里面的内容按照key排序,使用比较器排序。(大小有序) HashMap:双值 key value【小明 10】【小红 20】【小强 30】 TreeMap:【15 10】【22 20】【33 30】(有序)
-
所有的集合要么是Map接口下,要么是Collection接口下的。要么存放单值元素,要么存放双值元素。
-
接口不能实例化对象,具体的类才能实例化对象。
3.迭代器
3.1 迭代器模式
-
迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
-
作用
(1)遍历集合时,不暴露集合内部结构。 (2)为遍历不同的聚合结构提供一个统一的接口。
-
使用方式
(1)首先得有集合 (2)迭代器角色(具体迭代器角色)
3.2 迭代器模式的角色构成
-
迭代器角色(Iterator)
定义遍历元素所需要的方法,一般来说会有这么三个方法: next()方法:取到当前的元素,并且移动到下一个元素的位置; hasNext()方法:判断遍历是否结束; remove()方法:删除当前对象;
-
具体迭代器角色(Concrete Iterator)
实现迭代器接口中定义的方法,完成集合的迭代。ArrayList中的Itr。
-
容器角色(Aggregate)
一般是一个接口,提供一个iterator()方法,例如Java中Collection接口,List接口,Set接口等。
-
具体容器角色(Concrete Aggregate)
就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
4. 快速失败机制
在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。
-
原理
迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用modCount变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hasNext()/next()遍历下一个元素之前,都会检测modCount遍历是否为expectedModCount值,是的话就返回遍历;否则抛出异常,终止遍历。
-
注意
这里异常的抛出条件是检测到modCount != expectedModCount这个条件。如果集合发生变化时修改modCount值刚好又设置为了 expectedModCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
-
场景
java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。
5. 安全失败机制
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
-
原理
由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中队员集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
-
缺点
基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样的,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
-
场景
java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。