Fork me on GitHub

十九.集合(容器)

集合总接口层次图

容器是什么呢?就是容纳物体的东西,如我们之前学的数组就是一种容器。

数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。

数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。比如:我们在一个用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?我们在写程序时是无法确定的。因此,在这里就不能使用数组。

基于数组并不能满足我们对于“管理和组织数据的需求”,所以我们需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是我们今天要学习的容器,也叫集合(Collection)。以下是容器的接口层次结构图:

image

为了能够更好的学习容器,我们首先要先来学习一个概念:泛型

泛型

泛型是JDK1.5以后增加的,它可以帮助我们建立类型安全的集合。在使用了泛型的集合中,遍历时不必进行强制类型转换。JDK提供了支持泛型的编译器,将运行时的类型检查提前到了编译时执行,提高了代码可读性和安全性。

泛型的本质就是“数据类型的参数化”。 我们可以把“泛型”理解为数据类型的一个占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @author RickYinPeng
* @ClassName Test_01_Generic
* @Description 测试泛型
* @date 2019/1/22/10:47
*/
public class Test_01_Generic {
public static void main(String[] args) {
MyCollection<String> mc = new MyCollection<String>();
mc.set("尹鹏",0);

/**
* 这里报错,前面我们定义的泛型是String,那么我们Set的时候也必须是String
*/
/*mc.set(9888,1);
int o = (int) mc.get(1)*/;

String s = mc.get(0);

List list = new ArrayList();
}
}
class MyCollection<E>{
Object[] objs = new Object[5];
public void set(E e,int index){
objs[index] = e;
}
public E get(int index) {
return (E) objs[index];
}
}

Collection接口

Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。

Collection接口中定义的方法

image

由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法

List特点和常用方法

List是有序、可重复的容器。 有序:List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。

可重复:List允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器。

除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方法,参见下表:
image

List接口常用的实现类有3个:ArrayList、LinkedList和Vector。
1. ArrayList:底层使用数组实现
2. LinkedList:底层使用链表实现
3. Vector:底层使用数组实现,而且是线程安全

### List的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class TestList {
/**
* 测试add/remove/size/isEmpty/contains/clear/toArrays等方法
*/
public static void test01() {
List<String> list = new ArrayList<String>();
System.out.println(list.isEmpty()); // true,容器里面没有元素
list.add("花花");
System.out.println(list.isEmpty()); // false,容器里面有元素
list.add("尹鹏");
list.add("");
System.out.println(list);
System.out.println("list的大小:" + list.size());
System.out.println("是否包含指定元素:" + list.contains("尹鹏"));
list.remove("花花");
System.out.println(list);
Object[] objs = list.toArray();
System.out.println("转化成Object数组:" + Arrays.toString(objs));
list.clear();
System.out.println("清空所有元素:" + list);
}
public static void main(String[] args) {
test01();
}
}


### 两个List之间的元素处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TestList {
public static void main(String[] args) {
test02();
}
/**
* 测试两个容器之间元素处理
*/
public static void test02() {
List<String> list = new ArrayList<String>();
list.add("高淇");
list.add("高小七");
list.add("高小八");

List<String> list2 = new ArrayList<String>();
list2.add("高淇");
list2.add("张三");
list2.add("李四");
System.out.println(list.containsAll(list2)); //false list是否包含list2中所有元素
System.out.println(list);
list.addAll(list2); //将list2中所有元素都添加到list中
System.out.println(list);
list.removeAll(list2); //从list中删除同时在list和list2中存在的元素
System.out.println(list);
list.retainAll(list2); //取list和list2的交集
System.out.println(list);
}
}


### List中操作索引的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TestList {
public static void main(String[] args) {
test03();
}
/**
* 测试List中关于索引操作的方法
*/
public static void test03() {
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
System.out.println(list); // [A, B, C, D]
list.add(2, "高");
System.out.println(list); // [A, B, 高, C, D]
list.remove(2);
System.out.println(list); // [A, B, C, D]
list.set(2, "c");
System.out.println(list); // [A, B, c, D]
System.out.println(list.get(1)); // 返回:B
list.add("B");
System.out.println(list); // [A, B, c, D, B]
System.out.println(list.indexOf("B")); // 1 从头到尾找到第一个"B"
System.out.println(list.lastIndexOf("B")); // 4 从尾到头找到第一个"B"
}
}


## ArrayList集合的底层源码

请移步Java基础提高