5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

一、类集

类集就是一组动态的对象数组,说类集可能不好理解,类集又称容器,容器顾名思义就是放东西的地方。

类集就是为了让我们更加简洁,方便的存放、修改、使用数据的。

二、Collection接口

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

我们看下文档中的描述,Collection这个接口主要是代表容器的规范,主要用于实现其他具体功能的子接口。

Collection更像是一个抽象出来的规则,它代表了一种标准和规范,具体的容器的实现是依靠子接口去实现的。

Collection只是定义了一种标准,其他的子接口必须按照这个标准来,也可是说是其他子类必须完成这个标准所规定的内容。

三、List接口、ArrayList类

List是Collection的一个子接口,在Collection原有的基础上进行了一定扩充,List中存放的内容是允许重复的。

我们来看文档对它的描述:

An ordered collection (also known as a sequence).

有序集合(也称为序列)。更直白一点就是一个放数据的容器,而且是有序的。

ArrayList是一个类,它继承自abstractArrayList,而abstractArrayList继承了Collection接口。

这关系有点乱,我们来看下图。

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

ArrayList看名字就知道和数组有关,新建一个ArrayList对象里面存储方式是数组。

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

这就是ArrayList中存储数据的数组。

我们先介绍一个方法,就是向容器中添加数据。

add(E e);

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

我们简单的看下源码,将传递进来的数据e添加数组elementData中,然后size++,就是一个普通的往数组后面添加元素的操作。

这里E是泛型,由使用时决定。

 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.add(1);
list.add(2);
list.add(3);
System.out.println(list);
}
}
运行结果:
[1, 2, 3]

代码中为什么要像List<Integer> l = new ArrayList<>();这样写?

而不直接ArrayList<Integer> i = new ArrayList<>()写?

可能有的人会有疑问,我一开始也有疑问。

关于这个问题可以参考:

https://www.cnblogs.com/huang-changfan/p/9613971.html

四、ArrayList类中常用方法

1、E get(int index);

返回指定数组指定下标的数据。

 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.add(1);
list.add(2);
list.add(3);
System.out.println(list.get(1));
}
}
运行结果:
2

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

我们看下源码中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)

返回指定内容的下标。

 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.add(1);
list.add(2);
list.add(3);
System.out.println(list.indexOf(3));
}
}
运行结果:
2

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

我们来看indexOf的源码,首选项判断是否为为空,是的话对数组遍历,如果有元素为空的话直接返回该元素下标。

反之,也是遍历,调用Object类中equals方法进行比较,注意Object中的equals方法是比较值用的是==。

4.remove(int index)

删除指定位置的元素。

 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.add(1);
list.add(2);
list.add(3);
list.remove(2); System.out.println(list);
System.out.println(list.size());
}
}
运行结果:
[1, 2]
2

我们来看下源码是怎么操作的。

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

我们看实现功能的核心部分,

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);

 import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; public class test {
//ArrayList Collection
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
list.add(1);
list.add(2);
list.add(3); System.out.println(list);
System.out.println(list.size());
}
}
 运行结果:
[1, 2, 3]
3

可以看出实现的功能是相同的。

但我们来看下源码:

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

5.1 java类集(java学习笔记)Collection、List接口及ArrayList、LinkedList类。

可以看到,这里添加元素并不是想ArrayList那样添加,通过链表的连接添加数据。

我可以尝试下,自己实现一个简易的LinkedList

下面写的简易版的LinkedList实现了添加,正序遍历和反序遍历,remove,get。

 public class test {
//ArrayList Collection
public static void main(String[] args) {
MyLinkedList<Integer> list = new MyLinkedList<>();
list.add(1);
list.add(2);
list.add(3);
list.fOutLinked();
// System.out.println(list.size);
//list.get(1);
list.remove(1);
list.fOutLinked(); }
} class MyLinkedList<E>{ //自己实现的简易LinkedList类
int size = 0;
private Node<E> first;//创建用于存放头结点结点。
private Node<E> last; //创建用于存放末尾结点的结点。
public void add(E e){ //添加方法
if(first == null){ //如果没有头结点,即什么结点都没有创建,是第一次创建结点时
Node<E> n = new Node<E>();//首先创建一个结点
n.setDate(e); //将该节点中的数据区方法传递进来的数据
n.setFirst(null); //并将头结点的头部放入null
n.setLast(null); //同时将头结点的尾部放入null
first = n; //然后将n给头结点和尾结点
last = n; //因为这时双向链表,所以在只有一个结点的时候,
size++; //该节点即是头结点,也是尾结点。
}else{
Node<E> n = new Node<E>();//如果已经有了结点。
n.setFirst(last); //则将新建结点的头部存放前一结点的信息
last.setLast(n); //然后将前一个结点的尾部设置为新建结点。
n.setDate(e); //然后将新建结点中的数据区放入传递进来的数据
n.setLast(null); //然后将新建结点的尾部放null
last = n; //然后将新建结点作为尾结点
size++;
}
}
public void fOutLinked(){//正序输出
Node<E> f = new Node<E>();
f= first; //新建一个结点,并将头结点给它。
System.out.println(f.getDate()); //打印该结点的数据。
while(f.getLast()!= null){ //如果该节点的尾部不为null则:
f = f.getLast(); //将f结点之后的一个结点赋给f,此时f存放的是f之后的结点。
System.out.println(f.getDate());//打印出该节点的数据
}
}
public void lOutLinked(){ //逆序与正序思想相同
Node<E> l = new Node<E>();
l= last;
System.out.println(l.getDate());
while(l.getFirst()!= null){
l = l.getFirst();
System.out.println(l.getDate());
}
} public void get(int index){//获取指定位的数据。
int temp = 0;
Node<E> f = new Node<E>();
f= first;
if(index == 0){//如果index ==0 ,直接打印头结点的数据。
System.out.println(f.getDate());
}else{
while(f.getLast()!= null){ //反之,判断下该节点的尾部是否为空
f = f.getLast(); //不为null则将f结点存放后一结点信息。
temp++; //同时temp++,每次后移一个结点就加一
if(temp == index){ //如果temp == index,则代表找到了指定位的结点
System.out.println(f.getDate());//打印
}
}
} } public void remove(int index){//移处指定位的结点
Node<E> l = new Node<>(); //首先创建两个结点,用于存放头结点,和尾结点。
Node<E> f = new Node<>();
int count = 0; //判断当前结点是否为指定结点,每次后移一位会加一
f = first;
l = last;
if(index == 0){//如果移除的结点为头结点,则将头结点后面结点的头部设置为null
first.getLast().setFirst(null);//first.getLast()代表头结点后的结点,
}else //然后后面的结点设置本身的头部为null
if(index == size - 1){ //如果移除的结点为尾结点
last.getFirst().setLast(null);//则将尾结点之前结点的尾部设置为null。
}else{
while(count<size-1){//如果要移除的结点既不是头结点,也不是尾结点。
f = f.getLast();//那么从头结点开始遍历,
count++;//每后移一个结点,count++
if(count == index){ //如果找到了指定结点
f.getFirst().setLast(f.getLast()); //将指定结点的前一个结点的尾部,设置为指定结点的后一个结点。
f.getLast().setFirst(f.getFirst());//将指定结点的后一个结点的头部,设置为指定结点的前一个结点。
f.setFirst(null);//将指定结点的头部和尾部设置为null
f.setLast(null);
}
} }
}
} class Node<E>{ //结点
private Node<E> first; //存放前一结点的结点
private E date; //存放信息
private Node<E> last; //存放后一个结点的结点
public Node<E> getFirst() {//后面就是一些set,get方法
return first;
}
public void setFirst(Node<E> first) {
this.first = first;
}
public E getDate() {
return date;
}
public void setDate(E date) {
this.date = date;
}
public Node<E> getLast() {
return last;
}
public void setLast(Node<E> last) {
this.last = last;
}
}
上一篇:Java精选笔记_集合概述(Collection接口、Collections工具类、Arrays工具类)


下一篇:JAVA中Collection接口和Map接口的主要实现类