集合:List接口的实现类(ArrayList、LinkedList、Vector)

1、List接口

(1)特点

  • 有序(插入和取出的顺序相等,因为有一个整数索引记录了元素的插入的位置)
  • 允许有重复的元素(调用equals方法返回true,允许有多个null)
    @Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(null);
             list.add(111);
             list.add(null);
             System.out.println(list);
    }
[123, 123, null, 111, null]

(2)与collection相比的特有方法

  • add(index,元素):在指定的位置插入
  • get(index):获取指定位置的元素
  • remove(index):删除指定位置的元素
  • set(index,新元素):修改指定位置的元素
  • subList(index,index):根据索引范围截取一个子集合
  • indexOf(元素):查找

与collection接口的最大区别就是list接口的操作会涉及到索引。

2、List接口的特有方法

(1)增加元素

指定位置插入

    @Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(null);
             list.add(111);
             list.add(null);
             list.add(1,"zhai");
             System.out.println(list);
    }
[123, zhai, 123, null, 111, null]

(2)下标越界异常

    @Test
    public void test1() {
             List list=new ArrayList();
             list.add(1,"zhai");
             System.out.println(list);
    }
java.lang.IndexOutOfBoundsException: Index: 1, Size: 0

程序源码:

判断索引的范围是否有效:

 索引大于集合大小或索引小于0,抛出异常:

 (2)删除元素

根据索引删除:

@Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(null);
             list.add(111);
             list.add(null);
             list.remove(2);
             System.out.println(list);
    }
[123, 123, 111, null]

默认按照索引删除:

@Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(8848);
             list.add(111);
             list.add(1);
             list.remove(1);
             System.out.println(list);
    }
[123, 8848, 111, 1]

在可以按照索引删除和按照元素删除的情况下,List默认按照索引删除元素

 编译器提示的也是按照索引删除,因为不存在100索引,会抛出异常:

java.lang.IndexOutOfBoundsException: Index: 100, Size: 5

 查看源码可以看出remove存在方法的重载,index是基本数据类型的数据,如果是按照内容移除还要讲int类型转换为Integer类型,显然是不合理的

 @Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(8848);
             list.add(111);
             list.add(100);
             list.remove(new Integer(100));
             System.out.println(list);
    }
[123, 123, 8848, 111]

直接传递引用类型的数据,不会再调用参数列表为int类型的remove方法,而是直接调用按照内容移除元素的方法

总结:

如果元素的类型为int的,默认按照索引删除;如果想要按照指定的元素删除,则需要装箱后再删除

(3)修改

(1)修改指定位置的元素:

@Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(8848);
             list.add(111);
             list.add(100);
             list.set(1,"2020年8月5日08:41:50");
             System.out.println(list);
    }
[123, 2020年8月5日08:41:50, 8848, 111, 100]

(4)查找

@Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(8848);
             list.add(111);
             list.add(100);
             System.out.println(list.indexOf(100));
    }
4

3、list接口的遍历方式

(1)迭代器

  @Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(8848);
             list.add(111);
             list.add(100);
        Iterator iterator=list.iterator();
        while (iterator.hasNext()){
            Object l=iterator.next();
            System.out.println(l);
        }
    }
123
123
8848
111
100

(2)增强for

 @Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(8848);
             list.add(111);
             list.add(100);
             for (Object l:list){
                 System.out.println(l);
             }
    }
123
123
8848
111
100

(3)普通for

@Test
    public void test1() {
             List list=new ArrayList();
             list.add("123");
             list.add("123");
             list.add(8848);
             list.add(111);
             list.add(100);
             for (int i=0;i<list.size();i++){
                 System.out.println(list.get(i));
             }

    }
123
123
8848
111
100

因为list接口可以操作索引,因此,可以采用for循环遍历list接口的数据

4、ArrayList源码分析与底层结构

(1)可变长度的实现

代码:

@Test
    public void test1() {
             ArrayList list=new ArrayList();
             for (int i=0;i<=10;i++){
                 list.add("join"+i);
             }
                for (int i=11;i<=20;i++){
                    list.add("join"+i);
                }
                System.out.println(list);
    }

运行至集合中有10个元素:

 底层实际上是可变数组,该数组是Object类型的,初始的时候数组的长度为0,第一次扩容为10,第二次为15,第三次增加为22... ... 也就是说数组的长度以1.5倍的速度增加

源码分析:

 因为该变量是一个长度为0的数组:

 add方法:

 

 数组的扩容:

 elementData是存储集合的可变数组:

5、Vector底层与ArrayList的对比

(1)相同点

底层都是可变数组

(2)不同点

版本:

  • ArrayList:新
  • Vector:旧

线程安全:

  • ArrayList:线程不安全,效率高
  • Vector:线程安全(同步),效率低

扩容倍数:

  • Vector变为原来的2倍

  •  Arraylist:变为原来的1.5倍

6、LinkedList底层和源码分析

(1)LinkedList

底层是双向链表

  • 每个结点维护三个属性:item代表自己,next代表下一个元素,prev代表前一个元素
  • LinkedList维护两个重要属性:first和last,分别指向首结点和尾结点

(2)源码分析

 构造器为空:

  public LinkedList() {
    }

添加元素的源码分析:

   public boolean add(E e) {
        linkLast(e);
        return true;
    }
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

new Node<>(l, e, null):三个参数分别是:前一个、当前元素、下一个

尾插法添加元素,第一次添加first就是第一个元素,不为空的时候在last后添加

(3)与ArrayList的对比

底层结构

ArrayList:可变数组

LinkedList:双向链表

增删的效率

ArrayList:需要移动大量的元素效率较低

LinkedList:插入时不需要移动内存中的元素,只需要改变引用的指向即可,所以插入或删除的效率高

改查的效率

ArrayList:具有索引,查询或修改快

LinkedList:查询时从头部开始,一个一个地找,查询效率较低(虽然有索引,但是查询方式不同,LInkedList必须一个一个查询,而ArrayList可以直接获取相应索引的元素)

使用场景

ArrayList:使用在查询比较多,插入删除较少的情况

LinkedList:使用在查询比较少,插入和删除比较多的情况

总结:

1、List的接口的三个实现类的选择

如果考虑线程安全的问题,采用Vector

不考虑线程安全的问题的话,如果查询较多用ArrayList,增删较多用LinkedList

原文地址:https://www.cnblogs.com/zhai1997/p/13438829.html