List 接口的常用实现

List

List 是 Collection 的一个子接口,它拥有 Collection 中的所有方法,由于具有索引,所以可以进行一些索引操作

常用 API

方法说明
boolean add(int index, E e)添加元素至指定索引
boolean addAll(Collection c)
boolean addAll(int index, Collection c)添加集合至指定索引
Object get(int index)获取指定索引位置元素
int indexOf(Object o)获取元素第一次出现的索引
int lastIndexOf(Object o)获取元素最后一次出现的索引
Object remove(int index)删除指定索引位置元素,返回删除值
Object set(int index, E element)修改指定索引位置元素值,返回原值
List<E> subList(int fromIndex, int toIndex)获取索引范围内的元素,左开右闭
boolean equals(Object o)将指定的对象与此列表进行比较以获得相等性
ListIterator<E> listIterator()返回列表中的列表迭代器(按适当的顺序)
ListIterator<E> listIterator(int index)从指定位置开始,返回列表的列表迭代器
public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("1");
    
    // 在指定索引处添加元素
    list.add(1, "3");
    
    // 删除指定索引处元素并返回
    String remove = list.remove(1);
    
    // 修改指定索引处元素并返回原值
    String set = list.set(1, "3");
    
	// 获取指定索引处元素
    String s1 = list.get(1);

    for (String s : list) {
        System.out.println(s);
    }
}

遍历

除了使用 Collection 中的两种遍历之外,List 可以使用独特的遍历方式

  1. 根据索引进行遍历
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
  1. 通过 ListIterator 迭代器遍历

列表迭代器除了可以往后遍历,还允许我们往前遍历

  • previous:返回上一个元素
  • hasPrevious:判断是否可以往前迭代
  • add(E e):插入元素

与 Iterator 接口相比,该接口增加了向前迭代的功能,还可以通过 add 方法添加元素

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");

    // 获取 列表迭代器
    ListIterator<String> stringListIterator = list.listIterator();

    while (stringListIterator.hasNext()) {
        String next = stringListIterator.next();
        System.out.println(next);
    }
}

自定义排序

由于 List 拥有索引,所以我们可以对其进行自定义规则的排序,如使用冒泡排序:

public static void bubbleSort(List list) {
    int size = list.size();
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j ++) {
            Book book1 = (Book)list.get(j);
            Book book2 = (Book)list.get(j + 1);
            if (book1.getPrice() > book2.getPrice()) {
                list.set(j, book2);
                list.set(j + 1, book1);
            }
        }
    }
}

ArrayList

概述

ArrayList 作为 List 的一个实现类,其有 (1) 元素有序、可重复、支持索引操作 等 List 接口的特点

除了 List 的几个特点之外,ArrayList 还允许 (2) 存储 null 值,且支持多个

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add(null);
    list.add(null);

    list.forEach(System.out::println);
}
null
null

在并发下,(3) ArrayList 是不安全的

常用API

除了拥有 List 的所有方法,还新增了一些方法

方法说明
Object clone()返回此 ArrayList 实例的浅拷贝
void ensureCapacity(int minCapacity)扩容
void foreach(Consumer<? super E> action)对每个元素执行特定操作
int lastIndexObf(Object o)
protected void removeRange(int fromIndex, int toIndex)
List<E> subList(int formIndex, int toIndex)返回一个子序列
void trimToSize()将列表容量调为当前大小

创建

ArrayList 的底层使用的是一个 可扩容的动态数组

transient Object[] elementData; // non-private to simplify nested class access

transient:瞬时的、短暂的,表示该属性不会被序列化

当调用无参构造创建 ArrayList 时,会对该数组进行初始化,默认为空数组,此时数组大小为0

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

扩容机制

当第一次使用 add 方法添加元素时,会对数组的大小进行扩容,此时数组大小为 10

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

private void ensureCapacityInternal(int minCapacity) { 
    //ensureCapacityInternal:确认有容量能够添加
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
        // 判断是否为空数组,如果是空数组,则返回默认容量和当前最少需要容量的最大值
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    // 如果不是空数组,则返回目前最少需要的容量
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    // 判断当前最少需要容量是否以及超过数组的大小
    // 如果超过了,则需要进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
  • DEFAULT_CAPACITY = 10:默认容量大小为 10

实际进行扩容的方法: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);
    
    // 扩容后,使用copyOf函数复制数组内容,并用null填充多余部分
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

扩容时,每次扩容的容量会是原来的 1.5 倍

如果创建数组的时候,使用的是有参构造,即传入了初始时容器的大小,则创建指定大小的数组;当需要扩容时,还是按照原来容量的 1.5 倍进行扩容

与 LinkedList 的区别

底层结构不同:ArrayList 底层为一个可变数组;LinkedList 底层为双向链表

效率的不同:ArrayList 改查快,增删慢;LinkedList 改查慢,增删快

内存结构的不同:ArrayList 必须开辟连续空间;LinkedList 无需开辟连续空间

与 Vector 的区别

相同点:两者的底层结构都是一个可变数组,默认大小都是 10

不同点:

  • 版本的不同:ArrayList 自 JDK 1.2 引入;Vector 自 JDK 1.0 引入
  • 效率差异:ArrayList 是线程不安全集合类,但其效率比 Vector 高;而 Vector 是线程安全集合类
  • 扩容倍数的不同:ArrayList 进行扩容时,按照 1.5 倍进行扩容,而 Vector 按照原来的 2 倍进行扩容

LinkedList

概述

LinkedList 实现了 List、Queue 接口

底层采用 双向链表,拥有 (1)增删快、查询慢 的特点

(2) LinkedList 是线程不安全的,其内部没有实现同步

由于 LinkedList 的底层是一个双向链表,所以可以对它进行一些首位操作

常用 API

LinkedList 除了拥有 List 所有的方法外,还可以通过一些特有的 API 对链表首尾进行一些操作

方法说明
void addFirst(E e)在该列表开头插入指定的元素
void addLast(E e)将指定的元素追加到此列表的末尾
E element()检索但不删除此列表的头
E getFirst()返回此列表中的第一个元素
E getLast()返回此列表中的最后一个元素
int lastIndexOf(Object o)
boolean offer(E e)将指定的元素添加为此列表的尾部
boolean offerFirst(E e)在此列表的前面插入指定的元素
boolean offerLast(E e)在该列表的末尾插入指定的元素
E peek()检索但不删除此列表的头
E peekFirst()检索但不删除此列表的第一个元素,空返回null
E peekLast()检索但不删除此列表的最后一个元素
E poll()检索并删除此列表的头
E pollFirst()
E pollLast()
E pop()从此列表表示的堆栈中弹出一个元素
void push(E e)将元素推送到由此列表表示的堆栈上
E removeFirst()从此列表中删除并返回第一个元素
boolean removeFirstOccurence(Object o)删除指定元素第一个出现的
E removeLast()
boolean removeLastOccurence(Object o)
public static void main(String[] args) {
    LinkedList<String> list = new LinkedList<>();
    list.add("1");
    list.add("2");

    list.addFirst("0"); // 在链表头添加元素
    list.addLast("4"); // 在链表尾添加元素

    String first = list.getFirst(); // 获取表头的元素
    String last = list.getLast(); // 获取表尾的元素

    String s = list.removeFirst(); // 删除表头的元素
    String s1 = list.removeLast(); // 删除表尾的元素

    // 清空链表
    while (!list.isEmpty()) {
        list.pop();
    }
}

底层结构

LinkedList 在底层维护了一个 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;
    }
}

并且维护了两个字段,用来指向首节点和尾节点

transient Node<E> first;
transient Node<E> last;

add 方法

add 方法:把元素加到队列最后

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

Vector

Vector 是 JDK 1.0 引入的一个 (1)线程安全 的集合类。其操作与 ArrayList 大致相同

(2)首次扩容的默认大小是10,元素有序、可重复、支持索引操作。底层的所有操作都带有 synchronized

在进行扩容的时候会 (3)以2倍进行扩容

特有 API:

方法说明
int capacity ()返回此向量的容量
void copyInto (Object[] array)将此向量复制到指定数组中
Enumeration<E> elements ()返回此向量的枚举

Stack

Stack:栈,继承自 Vector,底层为可扩容的数组

特性:先进后出(FILO,First In Last Out)

使用空参构造创建栈时,会创建一个空堆栈

栈包含了 Vector 中的全部API,也有一些特有 API

方法说明
boolean empty()判断此堆栈是否为空
E peek()获取栈顶元素
E pop()删除栈顶元素并返回
E push(E item)进栈操作
int search(Object o)查找元素o在栈中的位置,由栈底向栈顶方向数
public static void main(String[] args) {
    Stack<Integer> stack = new Stack<Integer>();
    for (int i = 0; i < 5; i++) {
        stack.push(i);
    }

    for (Integer integer : stack) {
        System.out.println(integer);
    }

    // 查找 2 在栈中的位置,并输出,下标自1开始
    int pos = stack.search(2);
    stack.pop();

    Integer peek = stack.peek();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值