【Java基础】集合中的List

一、什么是List

List是一个有序的集合,和set不同的是,List允许存储项的值为空,也允许存储相等值的存储项。
在这里插入图片描述
List继承了 Collection的接口,因此包括 Collection提供的各种方法,初次之外还扩展了自己的方法,如下图所示。
在这里插入图片描述

二、List的分类

(一)ArrayList

1、什么是ArrayList

ArrayList是一个数组实现的列表,底层是数组,由于数据是存入数组中的,所以它的特点也和数组一样,查询很快,但是中间部分的插入和删除很慢。

2、与Array的区别
  1. ArrayList内部封装了一个Object类型的数组,所以本质上没有区别,ArrayList的很多方法都是直接在内部数组的基础上调用的Array的方法。但是ArrayList还有其他功能因此更加复杂。
  2. ArrayList可以存储不同类型的数据,但是Array只能存储相同数据类型的数据。
  3. Array的长度不可变,ArrayList是变长的,虽然底层是通过扩展数据长度来实现的,但是表面上看是变长的。
  4. 存取和增删元素
    对于一般的引用类型来说,这部分的影响不是很大,但是对于值类型来说,往ArrayList里面添加和修改元素,都会引起装箱和拆箱的操作,频繁的操作可能会影响一部分效率。另外,ArrayList是动态数组,它不包括通过Key或者Value快速访问的算法,所以实际上调用IndexOf、Contains等方法是执行的简单的循环来查找元素,所以频繁的调用此类方法并不比你自己写循环并且稍作优化来的快,如果有这方面的要求,建议使用Hashtable或SortedList等键值对的集合。
3、方法探究
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.默认长度
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     */
     //从这里可以看到,ArrayList的底层是由数组实现的,并且默认数组的默认大小是10
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

ArrayList的构造函数
ArrayList有2个构造函数,一个是默认无参的,一个是有参的(数组大小)
如果我们可以预估数组大小,最好使用使用有参构造函数。因为如果使用无参的构造函数,会首先把EMPTY_ELEMENTDATA(默认是10)赋值给elementData
然后根据插入个数于当前数组size比较,不停调用Arrays.copyOf()方法,扩展数组大小造成性能浪费。

public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

add操作

//从尾部插入
 public boolean add(E e) {
		 //判断当前数据空间是否够插入数据
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将数组后移一位,将新元素赋值给此位置
        elementData[size++] = e;
        return true;
    }

public void add(int index, E element) {
        rangeCheckForAdd(index);
       //判断当前数据空间是否够插入数据
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //用System.arraycopy,把index开始所有数据向后移动一位
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
         //将新元素插入到index位置
        elementData[index] = element;
        size++;
    }

     private void ensureCapacityInternal(int minCapacity) {
     	//如果数组为空
            if (elementData == EMPTY_ELEMENTDATA) {
            //元素长度为默认长度和元素长度取较大值
                minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
            }

            ensureExplicitCapacity(minCapacity);
        }

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

            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
            //当元素个数大于数据长度的时候执行此方法
                grow(minCapacity);
        }

     private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //>>是移位运算符,相当于int newCapacity = oldCapacity + (oldCapacity/2),但性能会好一些,
        int newCapacity = oldCapacity + (oldCapacity >> 1);//根据老数组长度计算新数组长度,默认每次都是自增50%的大小
        //下面判断保证新数组元素的个数在正常范围内
        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);//用新数组继续下面的处理
    }

(二)Vector

Vector可以看做是线程安全版的ArrayList,利用synchronized来实现线程安全,所以他的一大弊端就是性能不高,而且这个类太古老已经被弃用。
因此如果我们对线程安全性要求不高就可以使用ArrayList,如果必须保证线程安全可以使用Collections中提供的方法,转变成线程安全的类。

(三)LinkedList

LinkedList底层是一个双向链表。LinkedList继承于AbstractSequentialList,和ArrayList一个套路。内部维护了3个成员变量,一个是当前链表的头节点,一个是尾部节点,还有是链表长度。

方法探究

add(E e)操作

//从指定位置插入(中间插入)
 public void add(int index, E element) {
        checkPositionIndex(index);
//如果插入位置和size相等相当于尾部插入
        if (index == size)
            linkLast(element);
        else
        //否则就是中部插入
            linkBefore(element, node(index));
    }
//尾部插入
 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
        原来尾节点的next指向新增的节点
            l.next = newNode;
            //数量加一
        size++;
        modCount++;
    }

 void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        //pred为插入节点的指向的前一个位置
        final Node<E> pred = succ.prev;
        //新增的节点
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        //如果指向前一个为空则说明这个链表为空,将新节点赋给头结点
        if (pred == null)
            first = newNode;
        else
        //否则将原来index的pred的next指向新节点
            pred.next = newNode;
            //数量加1
        size++;
        modCount++;
    }

indexOf操作
从头开始遍历整个链表,如果没有就反-1,有就返回当前下标

public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

remove操作

//无参的删除方法默认删除头结点
public E remove() {
        return removeFirst();
    }
//删除中间节点
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
//删除头结点
     public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

    //x的prev指向x的next
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

   
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        //删除头节点非常简单,就是把头节点的值清空,next清空
        f.item = null;
        f.next = null; // help GC
        //把nextNode设为头节点
        first = next;
        if (next == null)
            last = null;
        else
        //清空next的prev
            next.prev = null;
            //size减1
        size--;
        modCount++;
        return element;
    }

三、总结

List的特性:

  • 按顺序查找
  • 可以存null值
  • 可以存重复的值

对比LinkedList和ArrayList的实现方式不同,可以在不同的场景下使用不同的List 。ArrayList是由数组实现的,方便查找,返回数组下标对应的值即可,适用于多查找的场景 ,LinkedList由链表实现,插入和删除方便,适用于多次数据替换的场景。
ArrayList、LinkedList、Vector的区别和实现原理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据您提供的引用内容,我们可以使用Java Stream来筛选Java List集合的对象。以下是几种常见的筛选方法: 方法一: 为了从集合筛选出满足特定条件的对象,您可以使用filter()方法。例如,如果您想筛选出性别为男性的对象,可以按如下方式操作: ``` List<Student> maleStudents = list.stream() .filter(student -> Objects.equals(student.getSex(), "男")) .collect(Collectors.toList()); ``` 该方法使用filter()方法根据指定的条件进行筛选,并使用collect()方法将结果收集到新的List。 方法二: 如果您想要对筛选结果进行排序,可以使用sorted()方法。例如,按照姓名对性别为男性的对象进行排序: ``` List<Student> sortedMaleStudents = list.stream() .filter(student -> Objects.equals(student.getSex(), "男")) .sorted(Comparator.comparing(Student::getName)) .collect(Collectors.toList()); ``` 该方法先使用filter()方法筛选出满足条件的对象,然后使用sorted()方法按照指定的属性进行排序。 方法三: 如果您想要去除重复的对象,可以使用distinct()方法。例如,根据性别去除重复的对象: ``` List<Student> uniqueStudents = list.stream() .distinct() .collect(Collectors.toList()); ``` 该方法使用distinct()方法去除重复的对象,并使用collect()方法将结果收集到新的List。 以上是几种常见的Java List集合对象筛选方法,您可以根据自己的需求选择适合的方法进行使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值