一.单列集合
1.概览
Java 集合,也叫作容器,主要是由两大接口:一个是 Collecion接口,主要用于存放单一元素;另一个是 Map
接口,主要用于存放键值对.Collection是单列集合的根接口,Collection接口下面又有三个子接口List接口、Set接口和Queue接口,他们下面分别有不同的实现类,如下图所示:
Java 单列集合框架概览
注:图中只列举了主要的继承派生关系,并没有列举所有关系。比方省略了AbstractList
, NavigableSet
等抽象类以及其他的一些辅助类
2. List, Set, Queue三种集合的区别
List
系列集合:存储的元素是有序,可重复,有索引的
Set
系列集合:存储的元素是无序,不可重复,无索引的
Queue
系列集合: 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的.
3.了解List
List集合下有三个实现类
ArrayList
:Object[]
数组Vector
:Object[]
数组LinkedList
:双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)
3.1List集合的常见方法
我们直接用代码演示一下
List<String> list = new ArrayList<>();
//1.boolean add(E e): 添加元素
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
System.out.println(list); //[张三, 李四, 王五, 赵六]
//2.void add(int index, E element): 在某个索引位置插入元素
list.add(2, "钱七");
System.out.println(list); //[张三, 李四,钱七, 王五, 赵六]
//3.E remove(int index): 根据索引删除元素, 返回被删除的元素
System.out.println(list.remove(2)); //钱七
System.out.println(list);//[张三, 李四, 王五, 赵六]
//4.E get(int index): 返回集合中指定位置的元素
System.out.println(list.get(3)); //赵六
//5. E set(int index, E element): 修改索引位置处的元素,修改后,会返回原数据
System.out.println(list.set(3,"老八")); //赵六
System.out.println(list); //[张三, 李四, 王五, 老八]
3.2List集合的遍历方式
List集合遍历方式一共有四种:
-
普通for循环
-
迭代器
-
增强for
-
Lambda表达式
还是用代码演示一下
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
//1.普通for循环
for(int i = 0; i< list.size(); i++){
//i = 0, 1, 2
String e = list.get(i);
System.out.println(e);
}
//2.增强for遍历
for(String s : list){
System.out.println(s);
}
//3.迭代器遍历
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
//4.lambda表达式遍历
list.forEach(s->System.out.println(s));
3.3ArrayList集合的底层原理
3.3.1ArrayList 和 Array的区别
ArrayList集合底层是基于数组结构实现的,也就是说当你往集合容器中存储元素时,底层本质上是往数组中存储元素. 那么ArrayList 和 Array(数组)的有区别么,我们带着这个问题来深入探索ArrayList集合.
ArrayList内部基于动态数组实现,比 Array
(静态数组) 使用起来更加灵活:
ArrayList
会根据实际存储的元素动态地扩容或缩容,而Array
被创建之后就不能改变它的长度了。ArrayList
允许你使用泛型来确保类型安全,Array
则不可以。ArrayList
中只能存储对象。对于基本类型数据,需要使用其对应的包装类(如 Integer、Double 等)。Array
可以直接存储基本类型数据,也可以存储对象。ArrayList
支持插入、删除、遍历等常见操作,并且提供了丰富的 API 操作方法,比如add()
、remove()
等。Array
只是一个固定长度的数组,只能按照下标访问其中的元素,不具备动态添加、删除元素的能力。ArrayList
创建时不需要指定大小,而Array
创建时必须指定大小。
下面是二者使用的简单对比:
// 初始化一个 String 类型的数组
String[] arr = new String[]{"张三", "李四", "王五"};
// 修改数组元素的值
arr[0] = "赵六";
System.out.println(Arrays.toString(arr));// [赵六, 李四, 王五]
// 删除数组中的元素,需要手动移动后面的元素
for (int i = 0; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
arr[arr.length - 1] = null;
System.out.println(Arrays.toString(arr));// [李四, 王五, null]
// 初始化一个 String 类型的 ArrayList
ArrayList<String> list = new ArrayList<>();
Collections.addAll( list,"张三","李四", "王五");
// 添加元素到 ArrayList 中
list.add("赵六");
System.out.println(list);// [张三, 李四, 王五, 赵六]
// 修改 ArrayList 中的元素
list.set(0, "钱七");
System.out.println(list);// [钱七, 李四, 王五, 赵六]
// 删除 ArrayList 中的元素
list.remove(0);
System.out.println(list); // [李四, 王五, 赵六]
3.3.2ArrayList的特点及底层
ArrayList的特点是查询快,增删慢. ArrayList可以直接通过索引定位到数据位置,查询任意数据的耗时是相同的,但是在增删数据时,需要将增删元素索引位置后的所有元素都依次向前或后移动一个位置,理论耗时就会很长.
前面通过对比得知,数组的长度是固定的,但是集合的长度是可变的,原理如下:
- 1.利用无参构造器创建集合,会在底层创建一个默认长度为0的数组
- 2.添加第一个元素时,底层会创建一个新的长度为10的数组
- 3.存满时,会扩容1.5倍
- 4.如果一次添加多个元素,1.5倍扩容依然放不下,则新创建的数组的长度以实际为准
- 5.我们需要注意的是,数组扩容,并不是在原数组上扩容(原数组是不可以扩容的),底层是创建一个新数组,然后把原数组中的元素全部复制到新数组中去
3.4 LinkedList集合的底层原理
LinkedList集合的底层数据结构是双链表,特点是查询慢,增删快.操作首尾元素,查询也不慢.双向链表在存储数据时会将每一个不同的数据存储在不同的结点中,每个节点又分为三个部分,第一部分用来存储前一个结点的地址值,第二部分存储具体数据的值,第三部分则用来存储下一个节点的地址值.
因此在使用LinkedList集合查询数据时,就相当于遍历集合查询,会从头节点开始访问,沿着链表的结构找下一个结点查询,知道找到所需要的数据才会停止.而增删数据,以新增数据为例,只需要将新增结点的上一个结点记录的下一个结点的地址值改为新增结点的地址值,再将新增结点的下一个结点,它记录的上一个结点的地址值改为新增结点的地址值即可.
flag: 以后会再出源码分析相关文章,配合图文讲解,更容易理解.