随笔都是学习笔记
随笔仅供参考,为避免笔记中可能出现的错误误导他人,请勿转载。

简介:

List是Collection的子接口,其最大的特点是允许保存重复的元素数据。

定义:

public interface List<E> extends Collection<E> 

需要清楚的是List子接口对于Collection的接口进行了方法的扩充。

- 获取指定索引的数据:E get(int index);
- 修改指定索引对象数据:E set(int index, E element);
- 返回ListIterator接口对象:ListIterator<E> listIterator();

从JDK1.9开始List接口中追加有一些static方法,方便用户的使用:

import java.util.List;

public class MAIN {
    public static void main(String[] args) {
        List<String> all = List.of("| WD |","| WD |","| WD |","| WD |","| WD |");   // 返回一个列表
        System.out.println(all);
        Object[] array = all.toArray(); 
        for (Object o : array) {
            System.out.println(o);
        }
    }
}

输出结果:

上面代码使用的方法不是List的传统用法,是在新版本之后添加的新功能。

 

ArrayList类:

ArrayList类是List接口中使用最多的子类,但是在使用这个子类的时候也是有前提要求的,对此展开理解;

定义:

public class ArrayList<E> 
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

构造方法:

通过类名可以发现,ArrayList应该封装的是一个数组。

无参构造:

public ArrayList()
 
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

 

 有参构造:

public ArrayList(int initialCapacity)
 
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {  // 初始长度存在则创建对象数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {  // 没有初始长度
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
private static final Object[] EMPTY_ELEMENTDATA = {};
transient Object[] elementData;  // 不可序列化对象数组
通过有参构造方法可以发现,在ArrayList里面所包含的就是一个对象数组。如果现在进行数据的追加可以发现ArrayList集合里面保存的对象数组的长度不够的时候,会进行新的数组开辟并将旧数组的内容拷贝到新数组之中,而后数组的开辟操作时下面是计算新数组长度的方法
private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,  // 新的数组大小
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {  // 计算新数组长度
        int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
        if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
            return prefLength;
        } else {
            return hugeLength(oldLength, minGrowth);
        }
    }

 

如果实例化ArrayList类对象的时候没有传递初始化长度,则默认情况下会使用一个空数组,但是在进行数据增加的时候发现创建的数组容量不够了,则会判断当前容量与默认容量的大小,使用较大容量的数值开辟新的数组:

JDK1.9之后 ArrayList默认的构造只会使用默认的空数组,使用的时候才会开辟数组,默认的开辟长度为10;
JDK1.9之前

ArrayList默认的构造会默认开辟长度为10的数组。

当ArrayList之中保存的容量不足的时候会采用成倍的方式进行增长,原始长度为10,下次增长后就是20,一次类推。

在使用ArrayList的时候,一定要估计数据量会有多少,如果超过了10个,最好是使用有参构造进行创建,以免产生大量的垃圾数组空间。

 

 

 

 

import java.util.ArrayList;
import java.util.List;

public class MAIN {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();  // 实例化List父接口
        list.add("www");
        list.add("www");
        list.add(".baidu.");
        list.add("com");
        System.out.println(list);
    }
}

 输出结果:

 

从上面的代码和结果可以发现List存储的特征:

    - 保存的顺序就是其存储的顺序

    - List集合中可以保存重复的数据

在JDK1.8之后为了方便输出处理,Iterable接口之中定义有forEach()方法:

输出支持(不是标准输出):

default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

使用forEach()方法:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class MAIN {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();  // 实例化List父接口
        list.add("www");
        list.add("www");
        list.add(".baidu.");
        list.add("com");
        list.forEach((str)->{
            System.out.println(str + "/");
        });
    }
}

输出结果:


自定义类对象保存:

import java.util.ArrayList;
import java.util.List;

class Person{
    private String name ;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class MAIN {
        List<Person> list = new ArrayList<>();  // 实例化List父接口
        list.add(new Person("张三",12));
        list.add(new Person("李四",15));
        list.add(new Person("王五",16));
        System.out.println(list.contains(new Person("王五",16)));
        System.out.println(list.remove(new Person("王五",16)));
        System.out.println(list);
    }
}

输出结果:

我们可以发现,此时无法删除或者查找到“王五”这个对象,这里就需要提醒一点,在使用remove()或contains()方法之前,一定要确保对象类之中重写了equals()方法;

当我们重写了equals()方法后输出结果:

此时就能发现“王五”这个对象可以被查找并删除了。

 

List子类:LinkedList类

LinkedList操作实现:

 

import java.util.LinkedList;
import java.util.List;

public class MAIN {
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();  // 实例化List父接口
        list.add("www");
        list.add("www");
        list.add(".baidu.");
        list.add("com");
        list.remove("www"); // 删除元素
        System.out.println("集合是否为空:" + list.isEmpty() + " \t集合元素个数 : " + list.size());  // 判断为空和元素个数
        list.forEach(System.out::print);
    }
}

 

输出结果:

 

此时程序的功能和ArrayList类是一样的,但是内部的实现机制是完全不同的;

在LinkedList类的构造方法里面并没有像ArrayList类那样提供有初始化大小的方法而只是提供有无参的构造方法:

 

 /**
     * Constructs an empty list.
*/
    public LinkedList() {
    }

 

下面观察add()方法的实现:

add()方法
public boolean add(E e) {
        linkLast(e);
        return true;
    }

在自定义链表的时候做了一个判断:传入的数据为null则不进行保存;

但是在LinkedList里面并没有这样的处理,而是所有的数据都可以保存;

而后此方法调用了LinkedLast()方法(在最后一个节点追加):

 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++;
    }

可以发现LinkedList里面保存的数据都是利用Node节点进行的封装处理,同时为了提高程序的执行性能,每一次都会保存上一个追加的节点(最后一个节点),这样就可以在增加数据的时候避免递归处理,在增加数据的时候要进行数据保存个数的增加。

通过上述分析可以发现LinkedList封装就是一个链表的实现。

 

 

面试题:

请问ArrayList 和LinkedList 有什么区别?

  - ArrayList 是数组实现的集合操作,而LinkedList 是链表实现的集合操作

  - 在使用List集合中的get()方法根据索引获取数据时,ArrayList 的时间复杂度为“ O(1) ”,而LinkedList 的时间复杂度为“ O(n) ”(n为集合的长度)  

  - ArrayList在使用的时候默认的初始化长度为10,如果不足则会采用2倍的形式进行容量的扩充,如果保存大数据量的时候可能会造成垃圾的产生以及性能的下降,但是这个时候可以通过LinkedList 进行保存。

 

 

Vector【JDK1.0】:

到了JDK1.2的时候,因为用户以及习惯于Vector,并且许多的系统类也是基于Vector实现的,所以类集框架将其保存了下来,并且让其多实现了一个List接口;

定义:

 

public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

 

继承结构与ArrayList 相同;

构造方法源代码:

public Vector()
public Vector() {
        this(10);
    }
如果Vector使用的是无参的构造方法,则一定会默认开辟一个10个长度的数组,而后其余的操作与ArrayList相同
public Vector(int initialCapacity)
 
public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
 
public Vector(int initialCapacity, int capacityIncrement)
public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

 

public synchronized void addElement(E obj)

 

public synchronized void addElement(E obj) {
        modCount++;
        add(obj, elementData, elementCount);
    }

 

可以发现Vector类之中的操作方法采用的都是synchronized 同步处理,而ArrayList并没有进行同步处理,所以Vector类之中的方法在多线程访问的时候属于线程安全的,但是性能不如ArrayList高

Vector的使用:

 

import java.util.List;
import java.util.Vector;

public class MAIN {
    public static void main(String[] args) {
        List<String> list = new Vector<>();  // 实例化List父接口
        list.add("www");
        list.add(".baidu.");
        list.add("com");
        System.out.println("集合是否为空:" + list.isEmpty() + " \t集合元素个数 : " + list.size());  // 判断为空和元素个数
        list.forEach(System.out::print);
    }
}

 

输出结果:

 

 

 

 

 

未完待续、、、

 

posted on 2022-03-13 10:48  时间完全不够用啊  阅读(338)  评论(0编辑  收藏  举报