一、类集
类集就是一组动态的对象数组,说类集可能不好理解,类集又称容器,容器顾名思义就是放东西的地方。
类集就是为了让我们更加简洁,方便的存放、修改、使用数据的。
二、Collection接口
我们看下文档中的描述,Collection这个接口主要是代表容器的规范,主要用于实现其他具体功能的子接口。
Collection更像是一个抽象出来的规则,它代表了一种标准和规范,具体的容器的实现是依靠子接口去实现的。
Collection只是定义了一种标准,其他的子接口必须按照这个标准来,也可是说是其他子类必须完成这个标准所规定的内容。
三、List接口、ArrayList类
List是Collection的一个子接口,在Collection原有的基础上进行了一定扩充,List中存放的内容是允许重复的。
我们来看文档对它的描述:
An ordered collection (also known as a sequence).
有序集合(也称为序列)。更直白一点就是一个放数据的容器,而且是有序的。
ArrayList是一个类,它继承自abstractArrayList,而abstractArrayList继承了Collection接口。
这关系有点乱,我们来看下图。
ArrayList看名字就知道和数组有关,新建一个ArrayList对象里面存储方式是数组。
这就是ArrayList中存储数据的数组。
我们先介绍一个方法,就是向容器中添加数据。
add(E e);
我们简单的看下源码,将传递进来的数据e添加数组elementData中,然后size++,就是一个普通的往数组后面添加元素的操作。
这里E是泛型,由使用时决定。
1 import java.util.ArrayList;
2 import java.util.List;
3
4 public class test {
5 //ArrayList Collection
6 public static void main(String[] args) {
7 List<Integer> list = new ArrayList<>();
8 list.add(1);
9 list.add(2);
10 list.add(3);
11 System.out.println(list);
12 }
13 }
运行结果:
[1, 2, 3]
代码中为什么要像List<Integer> l = new ArrayList<>();这样写?
而不直接ArrayList<Integer> i = new ArrayList<>()写?
可能有的人会有疑问,我一开始也有疑问。
关于这个问题可以参考:
四、ArrayList类中常用方法
1、E get(int index);
返回指定数组指定下标的数据。
1 import java.util.ArrayList;
2 import java.util.List;
3
4 public class test {
5 //ArrayList Collection
6 public static void main(String[] args) {
7 List<Integer> list = new ArrayList<>();
8 list.add(1);
9 list.add(2);
10 list.add(3);
11 System.out.println(list.get(1));
12 }
13 }
运行结果:
2
我们看下源码中get方法的实现也很简单,首先是一个下标检查,判断下标是否合法(大于等于0小于size),然后直接返回数组中指定下标的元素。
2、addAll(Collection<? extends E> c)
添加一组数据,传递进去的泛型参数设置了上限。
import java.util.ArrayList;
import java.util.List;
public class test {
//ArrayList Collection
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list2.add(4);
list2.add(5);
list2.add(6);
list2.addAll(list); //将list里面的全部数据添加到list2中
System.out.println(list2);
}
}
运行结果:
[4, 5, 6, 1, 2, 3]
list中存有1,2,3.list2中存有4,5,6.然后将list中的内容添加到list2中,
list2中就有了4,5,6,1,2,3。
3、indexOf(Object obj)
返回指定内容的下标。
1 import java.util.ArrayList;
2 import java.util.List;
3
4 public class test {
5 //ArrayList Collection
6 public static void main(String[] args) {
7 List<Integer> list = new ArrayList<>();
8 list.add(1);
9 list.add(2);
10 list.add(3);
11 System.out.println(list.indexOf(3));
12 }
13 }
运行结果:
2
我们来看indexOf的源码,首选项判断是否为为空,是的话对数组遍历,如果有元素为空的话直接返回该元素下标。
反之,也是遍历,调用Object类中equals方法进行比较,注意Object中的equals方法是比较值用的是==。
4.remove(int index)
删除指定位置的元素。
1 import java.util.ArrayList;
2 import java.util.List;
3
4 public class test {
5 //ArrayList Collection
6 public static void main(String[] args) {
7 List<Integer> list = new ArrayList<>();
8 list.add(1);
9 list.add(2);
10 list.add(3);
11 list.remove(2);
12
13 System.out.println(list);
14 System.out.println(list.size());
15 }
16 }
运行结果:
[1, 2]
2
我们来看下源码是怎么操作的。
我们看实现功能的核心部分,
System.arraycopy(elementData, index+1, elementData, index,numMoved);
这是一个数组拷贝函数,例如我这里index为1,那么从2开始将后面的所有数据前移一位,最后一位加个null。
这样index = 1的那一项就被覆盖了,达到了删除效果。
其余remove函数也大同小异。
五、Vector类
Vector是一个比较古老的类,考虑到很多人员已经习惯了Vector,所以后来将其实现了List接口被保留了下来。
因为Vector也实现了List接口,所以用法与之前的无太大差异。
六、LinkedList类
LinkList同样是实现了List接口的一个子类。
它的存储方式是链表,相关的一些操作方法由于是实现List接口的,所以功能上与ArrayList大概是相同的。只是由于数据的存储方式不同
所以具体功能的实现与ArrayList有区别。
下面举个例子:
add(E e);
1 import java.util.ArrayList;
2 import java.util.LinkedList;
3 import java.util.List;
4
5 public class test {
6 //ArrayList Collection
7 public static void main(String[] args) {
8 List<Integer> list = new LinkedList<>();
9 list.add(1);
10 list.add(2);
11 list.add(3);
12
13 System.out.println(list);
14 System.out.println(list.size());
15 }
16 }
运行结果:
[1, 2, 3]
3
可以看出实现的功能是相同的。
但我们来看下源码:
可以看到,这里添加元素并不是想ArrayList那样添加,通过链表的连接添加数据。
我可以尝试下,自己实现一个简易的LinkedList
下面写的简易版的LinkedList实现了添加,正序遍历和反序遍历,remove,get。
1 public class test {
2 //ArrayList Collection
3 public static void main(String[] args) {
4 MyLinkedList<Integer> list = new MyLinkedList<>();
5 list.add(1);
6 list.add(2);
7 list.add(3);
8 list.fOutLinked();
9 // System.out.println(list.size);
10 //list.get(1);
11 list.remove(1);
12 list.fOutLinked();
13
14 }
15 }
16
17 class MyLinkedList<E>{ //自己实现的简易LinkedList类
18 int size = 0;
19 private Node<E> first;//创建用于存放头结点结点。
20 private Node<E> last; //创建用于存放末尾结点的结点。
21 public void add(E e){ //添加方法
22 if(first == null){ //如果没有头结点,即什么结点都没有创建,是第一次创建结点时
23 Node<E> n = new Node<E>();//首先创建一个结点
24 n.setDate(e); //将该节点中的数据区方法传递进来的数据
25 n.setFirst(null); //并将头结点的头部放入null
26 n.setLast(null); //同时将头结点的尾部放入null
27 first = n; //然后将n给头结点和尾结点
28 last = n; //因为这时双向链表,所以在只有一个结点的时候,
29 size++; //该节点即是头结点,也是尾结点。
30 }else{
31 Node<E> n = new Node<E>();//如果已经有了结点。
32 n.setFirst(last); //则将新建结点的头部存放前一结点的信息
33 last.setLast(n); //然后将前一个结点的尾部设置为新建结点。
34 n.setDate(e); //然后将新建结点中的数据区放入传递进来的数据
35 n.setLast(null); //然后将新建结点的尾部放null
36 last = n; //然后将新建结点作为尾结点
37 size++;
38 }
39 }
40 public void fOutLinked(){//正序输出
41 Node<E> f = new Node<E>();
42 f= first; //新建一个结点,并将头结点给它。
43 System.out.println(f.getDate()); //打印该结点的数据。
44 while(f.getLast()!= null){ //如果该节点的尾部不为null则:
45 f = f.getLast(); //将f结点之后的一个结点赋给f,此时f存放的是f之后的结点。
46 System.out.println(f.getDate());//打印出该节点的数据
47 }
48 }
49 public void lOutLinked(){ //逆序与正序思想相同
50 Node<E> l = new Node<E>();
51 l= last;
52 System.out.println(l.getDate());
53 while(l.getFirst()!= null){
54 l = l.getFirst();
55 System.out.println(l.getDate());
56 }
57 }
58
59 public void get(int index){//获取指定位的数据。
60 int temp = 0;
61 Node<E> f = new Node<E>();
62 f= first;
63 if(index == 0){//如果index ==0 ,直接打印头结点的数据。
64 System.out.println(f.getDate());
65 }else{
66 while(f.getLast()!= null){ //反之,判断下该节点的尾部是否为空
67 f = f.getLast(); //不为null则将f结点存放后一结点信息。
68 temp++; //同时temp++,每次后移一个结点就加一
69 if(temp == index){ //如果temp == index,则代表找到了指定位的结点
70 System.out.println(f.getDate());//打印
71 }
72 }
73 }
74
75 }
76
77 public void remove(int index){//移处指定位的结点
78 Node<E> l = new Node<>(); //首先创建两个结点,用于存放头结点,和尾结点。
79 Node<E> f = new Node<>();
80 int count = 0; //判断当前结点是否为指定结点,每次后移一位会加一
81 f = first;
82 l = last;
83 if(index == 0){//如果移除的结点为头结点,则将头结点后面结点的头部设置为null
84 first.getLast().setFirst(null);//first.getLast()代表头结点后的结点,
85 }else //然后后面的结点设置本身的头部为null
86 if(index == size - 1){ //如果移除的结点为尾结点
87 last.getFirst().setLast(null);//则将尾结点之前结点的尾部设置为null。
88 }else{
89 while(count<size-1){//如果要移除的结点既不是头结点,也不是尾结点。
90 f = f.getLast();//那么从头结点开始遍历,
91 count++;//每后移一个结点,count++
92 if(count == index){ //如果找到了指定结点
93 f.getFirst().setLast(f.getLast()); //将指定结点的前一个结点的尾部,设置为指定结点的后一个结点。
94 f.getLast().setFirst(f.getFirst());//将指定结点的后一个结点的头部,设置为指定结点的前一个结点。
95 f.setFirst(null);//将指定结点的头部和尾部设置为null
96 f.setLast(null);
97 }
98 }
99
100 }
101 }
102 }
103
104 class Node<E>{ //结点
105 private Node<E> first; //存放前一结点的结点
106 private E date; //存放信息
107 private Node<E> last; //存放后一个结点的结点
108 public Node<E> getFirst() {//后面就是一些set,get方法
109 return first;
110 }
111 public void setFirst(Node<E> first) {
112 this.first = first;
113 }
114 public E getDate() {
115 return date;
116 }
117 public void setDate(E date) {
118 this.date = date;
119 }
120 public Node<E> getLast() {
121 return last;
122 }
123 public void setLast(Node<E> last) {
124 this.last = last;
125 }
126 }