java集合框架-简易介绍

本文详细介绍了Java集合框架中的Iterable和Collection接口,重点讲解了List接口的实现,包括ArrayList和LinkedList的构造、操作方法以及扩容策略。ArrayList是基于数组实现的,支持快速随机访问,而LinkedList是双向链表,适合频繁的插入和删除操作。此外,文章还提到了线程安全的Vector和Stack类。
摘要由CSDN通过智能技术生成

1.集合框架
   所有集合实现类的最顶层接口为Iterable和Collection接口,再向下Collection分为了三种不同的形式,分别是List,Queue和Set接口,然后就是对应的不同的实现方式。
1.1 顶层接口Iterable
package java.lang;

import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
//支持lambda函数接口
import java.util.function.Consumer;
public interface Iterable<T> {
	//iterator()方法
    Iterator<T> iterator();
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

Iterable接口中只有iterator()一个接口方法,Iterator也是一个接口,其主要有如下两个方法hasNext()和next()方法。

boolean hasNext();
E next();
1.2 Collection接口
package java.util;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public interface Collection<E> extends Iterable<E> {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();
    boolean add(E e);
    boolean remove(Object o);
    boolean containsAll(Collection<?> c);
    boolean removeAll(Collection<?> c);
    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
    boolean retainAll(Collection<?> c);
    void clear();
    int hashCode();
    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

Collection的主要接口方法有:

int size();	集合大小
boolean isEmpty()	集合是否为空
boolean contains(Object o)	是否包含某元素
Iterator iterator()	迭代
Object[] toArray()	转数组
boolean add(E e)	添加元素
boolean remove(Object o)	移除某元素
boolean containsAll(Collection<?> c)	是否包含另一个集合
boolean removeAll(Collection<?> c)	移除集合中所有在集合c中的元素
boolean retainAll(Collection<?> c))	判断集合中不在集合c中的元素
void clear()	清空所有元素
2.List

List接口对Collection的扩展:多了添加方法add和addAll查找方法get,indexOf,set等方法,并且支持index下标操作

2.1 List接口

Collection与List的区别?

​ a. Collection和List最大的区别就是Collection是无序的,不支持索引操作,而List是有序的。Collection没有顺序的概念。

b. List中Iterator为ListIterator。

c. 由a推导List可以进行排序,所以List接口支持使用sort方法。

d. 二者的Spliterator操作方式不一样。

2.2 List的实现类ArrayList

​ ArrayList是List接口最常用的一个实现类,支持List接口的一些列操作。

2.2.1 ArrayList继承关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AAjBIlGu-1678588100627)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210508095712036.png)]

2.2.2 ArrayList组成
    private static final Object[] DEFAULTCAPACITY_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 == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

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

ArryList的 transient Object[] elementData; 是ArrayList保存数据的容器,ArrayList是由数组进行实现的。

2.2.3 ArrayList的构造方法
/**
* Default initial capacity.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final Object[] EMPTY_ELEMENTDATA = {};
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);
    }
}

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

ArrayList支持有参和无参的构造方法:

  • 无参构造,创建的是空集合
  • 有参构造,根据传入的参数进行创建,支持空集合创建。
2.2.4 ArryList的add方法
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

ensureCapcityInternal 方法主要是做了这几件事情:

  • 增加modCount的数值,modCount代表集合操作的次数。

  • 判断集合长度是否超过集合的长度,是扩容。

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

ensureExplicitCapacity 方法,集合修改次数累加,判断是否需要扩容

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

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
2.2.5 ArrayList的扩容

calculateCaoacity方法中

     private static final int DEFAULT_CAPACITY = 10;
    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  


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

在无参构造方法中,知道elementData 指向的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA 空数组,会直接扩容到DEFAULT_CAPACITY,DEFAULT_CAPACITY的长度为10,而当添加进ArrayList中的元素超过了数组能存放的最大值就会进行扩容。

在grow中可以得知,每次扩容但是1.5倍

 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);
    }
2.2.6 为什么elementData用transient修饰?
  1. transient的作用是该属性不参与序列化。
  2. ArrayList继承了标示序列化的Serializable接口。
  3. 对arrayList序列化的过程中进行了读写安全控制。
2.3 LinkedList

LinkedList是双向链表

2.3.1 LinkedList的继承关系

LinkedList既是List接口的实现也是Queue的实现(Deque),和ArrayList相比LinkedList支持的功能更多,可视作队列来使用

2.3.2 LinkedList结构

LinkedList由一个头节点和一个尾节点组成,分别指向链表的头部和尾部。

transient int size = 0;

transient Node<E> first;

/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;

Node类,由当前值item,和指向上一个节点prev和指向下个节点next的指针组成。并且只含有一个构造方法,按照(prev, item, next)这样的参数顺序构造。

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

数据结构中链表的头插法linkFirst和尾插法linkLast

/**
 * Links e as first element. 头插法
 */
private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

/**
 * Links e as last element. 尾插法
 */
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(index)方法的实现

判断index是更靠近头部还是尾部,靠近哪段从哪段遍历获取值。

Node<E> node(int index) {
    // assert isElementIndex(index);
    //判断index更靠近头部还是尾部
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

查询索引修改方法,先找到对应节点,将新的值替换掉老的值。

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

LinkedList要遍历找到该位置才能进行修改,而ArrayList是内部数组操作会更快

2.3.3 LinkedList修改方法

新增一个节点,采用尾插法将新节点放入尾部。

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

2.4 Vector

vactor组成和ArrayList基本一样

2.4.1 vector组成
//存放元素的数组
protected Object[] elementData;
//有效元素数量,小于等于数组长度
protected int elementCount;
//容量增加量,和扩容相关
protected int capacityIncrement;

2.4.2 vector线程安全性

vector是线程安全的,synchronized修饰的操作方法。

2.4.3 vector扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //扩容大小
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

扩容当构造没有capacityIncrement时,一次扩容数组变成原来两倍,否则每次增加capacityIncrement

2.5 Stack

Stack也是List接口的实现类之一,和Vector一样

2.5.1 Stack的继承关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5pmom0Ai-1678588100631)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210508175035827.png)]

​ Stack继承于Vector,也是List接口的实现类。Vector是线程安全的,因为其方法都是synchronized修饰的,所以处Stack从父类Vector继承而来的操作也是线程安全的。

2.5.2 Stack 的使用

Stack是栈的实现,主要操作为push入栈和pop出栈,而栈最大的特点就是LIFO(Last In First Out)

Stack<String> strings = new Stack<>();
strings.push("aaa");
strings.push("bbb");
strings.push("ccc");
System.err.println(strings.pop());

2.5.3 Stack方法分析

​ 用的push方法用的是Vector的addElement(E e)方法,该方法是将元素放在集合的尾部,

​ 而pop方法使用的是Vector的removeElementAt(Index x)方法,移除并获取集合的尾部元素,

​ Stack的操作就是基于线性表的尾部进行操作的

/**
 * Stack源码(Jdk8)
 */
public
class Stack<E> extends Vector<E> {
    public Stack() {
    }
	//入栈,使用的是Vector的addElement方法。    
	public E push(E item) {
        addElement(item);
        return item;
    }
    //出栈,找到数组最后一个元素,移除并返回。
    public synchronized E pop() {
        E       obj;
        int     len = size();
        obj = peek();
        removeElementAt(len - 1);
        return obj;
    }
    public synchronized E peek() {
        int     len = size();
        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }
    public boolean empty() {
        return size() == 0;
    }
    public synchronized int search(Object o) {
        int i = lastIndexOf(o);
        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }
    private static final long serialVersionUID = 1224463164541339165L;
}

3 Queue

queue是一种先进先出的数据结构,也就是first in first out。可以将queue看作一个只可以从某一段放元素进去的一个容器,取元素只能从另一端取

队列并没有规定是从哪一端插入,从哪一段取出

在这里插入图片描述

3.1 Deque是什么?

​ Deque英文全称是Double ended queue,也就是俗称的双端队列。就是说对于这个队列容器,既可以从头部插入也可以从尾部插入,既可以从头部获取,也可以从尾部获取

img

3.1.1Java中的Queue接口
package java.util;
public interface Queue<E> extends Collection<E> {
   	//集合中插入元素
    boolean add(E e);
    //队列中插入元素
    boolean offer(E e);
    //移除元素,当集合为空,抛出异常
    E remove();
    //移除队列头部元素并返回,如果为空,返回null
    E poll();
    //查询集合第一个元素,如果为空,抛出异常
    E element();
    //查询队列中第一个元素,如果为空,返回null
   
    E peek();
}

队列的操作不会因为队列为空抛出异常,而集合的操作是队列为空抛出异常。

Queue接口方法作用
boolean offer(E e)往队列中插入元素
E poll()队列中移除元素,并返回该元素
E peek()获取队列头部元素,但不做修改
3.1.2 Deque接口

相比Queue中的方法,方法名多了First和Last,First结尾的方法即从头部进行操作,Last即从尾部进行操作。

package java.util;

public interface Deque<E> extends Queue<E> {
	//deque的操作方法
    void addFirst(E e);
    void addLast(E e);
    boolean offerFirst(E e);
    boolean offerLast(E e);
    E removeFirst();
    E removeLast();
    E pollFirst();
    E pollLast();
    E getFirst();
    E getLast();
    E peekFirst();
    E peekLast();

    boolean removeFirstOccurrence(Object o);
    boolean removeLastOccurrence(Object o);
    // *** Queue methods ***
    boolean add(E e);
    boolean offer(E e);
    E remove();
    E poll();
    E element();
    E peek();
}

3.1.3 Queue,Deque的实现类

​ Java中关于Queue的实现主要是双端队列,Queue的实现有PriorityQueue,Deque在java.util中主要有ArrayDeque和LinkedList两个实现类,

两者一个是基于数组的实现,一个是基于链表的实现,LinkedList是一个双向链表,在此基础之上实现了Deque接口

3.2 PriorityQueue

PriorityQueue是Java中唯一一个Queue接口的直接实现,优先队列,其内部支持按照一定的规则对内部元素进行排序。

3.2.1 PriorityQueue继承关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eegULCG8-1678588100632)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210508182139971.png)]

​ PriorityQueue的继承实现关系中,是Queue的实现类,主要使用方式是队列的基本操作,Queue的基本原理,其核心是FIFO(First In First Out)原理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值