JavaSE List集合

JavaSE List集合


目录
  • List 集合是什么?有什么用?
  • List 集合怎么用?
    • 1 List 集合的继承图(常用部分)
    • 2 List 接口
      • 2-1 常用方法
      • 2-2 代码示例
        • 排序
    • 3 ArrayList 类
      • 3-1 介绍
      • 3-2 底层结构
      • 3-3 扩容机制
      • 3-4 常用方法
    • 4 LinkedList 类
      • 4-1 介绍
      • 4-2 底层结构
      • 4-3 常用方法
    • 5 Vector 类、Stack 类
      • 5-1 不建议使用
      • 5-2 替代方案
    • 6 不同 List 集合的使用场景

List 集合是什么?有什么用?

List 集合代表一个元素有序、可重复的集合。

集合中每个元素都有其对应的索引,元素的存放顺序和添加顺序一致,从索引 0 开始,依次递增。

List 集合会根据元素的数量自动扩容。

返回目录


List 集合怎么用?

1 List 集合的继承图(常用部分)

List 集合中常用的类有 ArrayList 和 LinkedList;

ArrayList 类底层是数组。

LinkedList 类底层是链表。同时该类也继承了 Deque 接口,代表可以作为双端队列和栈使用(不推荐,推荐使用 ArrayDeque)。

Vector 类和其子类 Stack,不推荐使用。

JavaSE List集合_第1张图片

返回目录


2 List 接口
2-1 常用方法

List 接口是 Collection 接口的子接口,可以使用 Collection 接口里的方法。

(需要看 Collection 接口的方法,可以看本人另一篇博客《JavaSE 集合入门》)

而且由于 List 是有序集合,因此 List 集合里增加了一些根据索引来操作元素的方法。

public interface List<E> extends Collection<E> {
    ...
    //将元素 element 插入到指定索引 index 处。
	void add(int index, E element);
    
    //将集合 c 插入到指定索引 index 处。
    boolean addAll(int index, Collection c);
    
    //返回指定索引处的元素
    E get(int index);
    
    //返回对象 o 在集合中第一次出现的位置索引
    int indexOf(Object o);
    
    //返回对象 o 在集合中最后一次出现的位置索引
    int lastIndexOf(Object o);
    
    //删除并返回指定索引处的元素
    E remove(int index);
    
    //替换索引处的元素,并返回旧元素
    E set(int index, E element);
    
    //根据传入的比较器,进行排序
    default void sort(Comparator c) {...}
    ...
}

注意1:List 判断两个对象相同是通过 equals() 方法。

public class ArrayList<E> ...{
    ... 
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                //通过 equals() 方法判断元素是否相等
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                //通过 equals() 方法判断元素是否相等
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    ...
}

返回目录

2-2 代码示例
  • 排序
List<String> list = new ArrayList<>();
list.add("abc");
list.add("ab");
list.add("a");
System.out.println(list); // [abc, ab, a]
list.sort((s1, s2) -> {
    return s1.length() - s2.length();
});
System.out.println(list); // [a, ab, abc]

返回目录


3 ArrayList 类
3-1 介绍

该类底层是数组。

该类是线程不安全的。

该类可以添加 null。

返回目录

3-2 底层结构

ArrayList 类底层是一个 Object[] 数组(数组名为:elementData) 。

默认为空数组,可以通过有参构造器,指定数组的初始容量。

public class ArrayList<E> ...{
    ... 
    transient Object[] elementData;
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    //无参构造器,初始化一个空数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    //有参构造器,初始化一个指定容量(≥0)的数组
    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);
        }
    }
    ...
}

返回目录

3-3 扩容机制

当使用无参构造器创建 ArrayList 时,初始容量为 0;第一次扩容为 10;以后每次扩容为原来容量的 1.5 倍。

当使用有参构造器创建 ArrayList 时,初始容量自定义(≥0);如果初始容量 ≥ 2,则每次扩容为原来容量的 1.5 倍;

如果初始容量为 0,则与无参构造器的情况相同;如果初始容量为 1,则第一次扩容为 2;以后每次扩容为原来容量的 1.5 倍。

public class ArrayList<E> ...{
    ...
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    private static final int DEFAULT_CAPACITY = 10;
    
    //1、添加元素
    public boolean add(E e) {
        //跳转到2
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    //2、确保数组容量够用(不明确,需要考虑数组为空数组的情况)
    private void ensureCapacityInternal(int minCapacity) {
        //跳转到3
        //跳转到4
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
   
    //3、计算所需容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //如果数组为空,则返回容量为 10
        //如果数组不为空,则返回容量为 minCapacity
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
	//4、确保数组容量够用(明确的)
	private void ensureExplicitCapacity(int minCapacity) {
        // modCount 记录数组被改变的次数
        modCount++;

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

	//5、执行扩容(扩容为原容量的1.5倍)
	private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 扩容1.5倍
        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);
  }
  ...
}

返回目录

3-4 常用方法
public class ArrayList<E> ...{
    ...
	// 构造器方法
    // 无参构造器,初始容量为 0,第一次扩容为 10
    public ArrayList() {...}
    // 有参构造器,初始容量自定义
    public ArrayList(int initialCapacity) {...} 
    // 有参构造器,复制集合中的元素
    public ArrayList(Collection<? extends E> c) {...} 
    
    // 继承 List 接口的方法
    // 继承 Collection 接口的方法
    ...
}

返回目录


4 LinkedList 类
4-1 介绍

该类底层是双向链表。

该类实现了 List 接口,可以根据索引来随机访问集合中的元素(效率低)。

该类同时还实现 Deque 接口,可以被当成双端队列、栈来使用(不推荐)。

该类可以添加 null,元素可以重复。

该类线程不安全。

返回目录

4-2 底层结构

LinkedList 底层是双向链表。

维护着指向头节点的 first 变量,指向尾节点的 last 变量。

节点类型为 Node 类,是 LinkedList 的内部类。

public class LinkedList<E> ...{
    ...
    //指向头节点,初始为Null
    transient Node<E> first;
    //指向尾节点,初始为Null
    transient Node<E> last;
    
    //在链表的末尾添加元素
	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++;
    }
    
    // 内部类 Node,作为链表节点的数据类型
    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;
        }
    }
    ...
}

返回目录

4-3 常用方法
public class LinkedList<E> ...{
    ...
    //1、作为双端队列
    //在队头、队尾添加节点,有返回值
	public boolean offerFirst(E e) {...}
    public boolean offerLast(E e) {...}
    //删除并返回队头、队尾元素
    public E pollFirst() {...}
    public E pollLast() {...}
    //返回队头、队尾元素,但不删除
   	public E peekFirst() {...}
    public E peekLast() {...}
    
    //2、作为栈
   	//入栈
    public void push(E e) {...}
	//出栈
    public E pop() {...}
    //查看栈顶元素
    public E peek() {...}     
    
    // 继承 List 接口的方法
    // 继承 Collection 接口的方法
    ...
}

返回目录


5 Vector 类、Stack 类
5-1 不建议使用

(1)Vector 是一个古老的集合(从 JDK 1.0 就有了),那时候 Java 还没有提供系统的集合框架,所以 Vector 里提供了一些名字很长的方法。

(2)Vector 是线程安全的,所以性能比 ArrayList 低。

(3)Stack 类继承了 Vector 类,因此它也是一个古老的 Java 集合类。它同样是线程安全、性能较差的。因此应该少用 Stack 类。

返回目录

5-2 替代方案

(1)需要保证 List 集合线程安全时,不推荐使用 Vector 类,建议使用 Collections 类将一个 ArrayList 对象变成线程安全的。

(2)需要使用“栈”时,不推荐使用 Stack 类,可以使用 LinkedList 类和 ArrayDeque 类。推荐使用 ArrayDeque 类。

返回目录


6 不同 List 集合的使用场景

改查操作多,使用 ArrayList。

增删操作多,使用 LinkedList。

不使用 Vector。

返回目录


你可能感兴趣的:(list,java)