Java | List集合

List接口

java.util.List接口继承Collection接口,是集合的一个重要的分支,我们平常所使用最多的就是这个集合,我们用实现类所实现这个接口的时候,习惯把这个接口的对象叫做List集合

List接口

List集合的特点:

1、它是一个元素存取有序的集合。
2、它是一个有索引的集合,可以通过索引精确的操作集合中的元素。
3、集合中允许有重复的元素。

List接口中自带的常用的方法:

public void add(int index, E element)将指定的元素,添加到该集合中的指定位置上。
public E get(int index)返回集合中指定位置的元素。
public E remove(int index)移除列表中指定位置的元素,返回被移除的元素。
public E set(int index, E emelent)用指定元素替换集合中指定位置的元素,返回是更新前的元素。

List接口中的常用方法,可以在它的所有的实现类中使用

List接口中常用的方法的使用

准备数据:

    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    list.add("d");
    list.add("e");
    System.out.println(list); //[a, b, c, d, e]

public void add(int index, E element)将指定的元素,添加到该集合中的指定位置

    //在bc之间加上一个*
    list.add(2, "*");
    System.out.println(list);   //[a, b, *, c, d, e]

public E get(int index)返回集合中指定位置的元素。

    //取出下标为2的元素
    String str = list.get(2);
    //String n = list.get(5);   java.lang.IndexOutOfBoundsException 数组下标越界异常
    System.out.println(str);  //c java中数组下标是从0开始的

public E remove(int index)移除列表中指定位置的元素,返回被移除的元素。

    //移除下标为3的元素
    String remove = list.remove(3);
    System.out.println(remove); //d
    System.out.println(list);   //[a, b, c, e]

public E set(int index, E emelent)用指定元素替换集合中指定位置的元素,返回是更

    //移除第一个元素,返回值是被移除的元素
    String set = list.set(0, "*");
    System.out.println(set);    //a
    System.out.println(list);   //[*, b, c, d, e]

ArrayList集合

java.util.ArrayList集合,在之前就使用过,但是一直没有介绍过,一直到现在再介绍,因为这个集合是以后我的在工作中用的最多的集合,并且它的大部分方法,在之前使用的时候已经用过了,所以在这里就不在多说他的使用方法了。


在这里主要说一下,他的数据结构,可以从名子中就可以看出来,他是数组实现的,是List接口的一个重要的实现类。

底层的实现:

List里面的数组:
transient Object[] elementData; // non-private to simplify nested class access
这个数组就是List集合里面的底层操作的数组。
List里面的长义:
private int size;
我们通常获得长度就是获得的这个就量的数据。
Arraylist里面的add方法

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!(增量)
        elementData[size++] = e;
        return true;
    }

ensureCapacityInternal(size + 1)这个方法的作用,就是查询集合中底层的数组的长度够不够,如果够,就继承执行下面的代码,如果不够,就得数组扩容,数组扩容后面再说。
elementData[size++] = e;这个就是把长度加一之后,存到数组的这个位置。
return true;如果没有发生异常,都是返回的true
ensureCapacityInternal(size + 1)方法:

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

这个方法是完成判断的主要的方法,它调用了calculateCapacity()方法来确定数组的长度。

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

DEFAULTCAPACITY_EMPTY_ELEMENTDATA 默认容器
DEFAULT_CAPACITY默认容量为10
当数组中没有元素的时候,会取默认长度和传进来的长度之中的最大值,致于为什么会这样,是因为这个方法不光add会调用,addAll也会调用这个方法,如果用的是Arraylist的默认方法创建的数组,那么说不定数组的长度会不够的,所以要判断一下。然后返回新数组的长度。

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

modCount这个变量的作用就是记录一下集合中元素操作的次数,每一次增加或删除的时候,都会加一。
当新数组长度,大于旧的数组长度的时候,就会通过grow()来拷贝数组。

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

这个方法就是拷贝数组的,这个方法就算到调用的是Arrays.copyOf(elementData, newCapacity),而这个方法的最底层是调用的System.arraycopy(),就是把创建一个新的数组,然后把原来数组中所有的内容都拷贝到新数组中去!


删除的方法,也是和增加的方法大同小异,所以就不说了。
所以说,ArrayList集合查询快,增删慢,就是因为他的底层都是数组操作,数组操作增加和删除的时候,都会创建新的数组,所以速度就会慢很多。


在我们使用List集合的时候,如果初始化数据过大,那么可以在new的时候,直接把长度传过去,这样可以提升一点效率。

LinkedList集合

java.util.LinkedList集合数据存储的结构是链表结构,方便元素的添加,删除。

链表结构,因为是链表结构,所以数据的存储不是在一个边续的内存里面,所以链表结构添加和删除元素的时候,是效率是非常高的。
简单说一下,LinkedList中的链表实现:
在集合中有一个内部娄:

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

链表其实就是这个Node表,所有的数组都是存到Node里面,item代表的就是当前元素,next就代表着下一个元素的地址,prev就代表着前一个元素的地址。


所以说,LinkedList集合添加和删除容易,就是因为在添加或删除元素的时候,只用new一个对象,然后修一个或两个对象里面的参数就行了。
具体的实现就不说了,但是很简单的。有兴趣的可以去网上找一下。

LinkedList的简单使用

因为是链表结构,所以第一个数据和最后一个数据是最特殊的,所以集合中有在量操作第一个和最后一个数据的方法,所以在这里,我就不说了,这些方法都可以查看API文档,我在这里说两个特殊一点的方法:

public void push(E e) 在列表的头部插入一个元素。
public E pop(); 移除列表里面的第一个元素。
准备数据:

    LinkedList<String> link  = new LinkedList<>();
    link.add("a");
    link.add("b");
    link.add("c");
    link.add("d");
    link.add("e");
    System.out.println(link);   //[a, b, c, d, e]

public void push(E e) 在列表的头部插入一个元素。

    link.push("*");
    System.out.println(link);   //[*, a, b, c, d, e]

public E pop(); 移除列表里面的第一个元素。

    String pop = link.pop();
    System.out.println(pop);    //a
    System.out.println(link);   //[b, c, d, e]

上面所说的两个LIst集合的实现类,都是在日常开发中经常使用到的,但是这两个实现类都不是同步,这两个实现类都是线程不安全的,所以涉及多线程的时候,要小心使用。



关注公众号,随时获取最新资讯

细节决定成败!
个人愚见,如有不对,恳请斧正!

posted @ 2020-04-08 00:55  一点浩然气~  阅读(287)  评论(0编辑  收藏  举报