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

 

一、类集

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

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

 

二、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<>()写?

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

 

关于这个问题可以参考:

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

 

 

四、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 }

 

posted @ 2018-09-09 16:59  gcmh  阅读(211)  评论(0编辑  收藏  举报