liuyulin
发布于 2024-02-02 / 27 阅读
0
0

Java源码

Java源码阅读

List

List继承于Collection接口,有顺序,有索引,允许存储重复元素,可以放入null

接口方法:

int size();  //获取大小
​
boolean isEmpty();  //判断是否为空
​
boolean contains(Object o);  //是否包含某个元素
​
Iterator<E> iterator(); //获取迭代器
​
Object[] toArray();  // 转化成为数组(对象)
​
<T> T[] toArray(T[] a);  // 转化为数组(特定位某个类)
​
boolean add(E e); //添加
​
boolean remove(Object o);  //移除元素
​
boolean containsAll(Collection<?> c); // 是否包含所有的元素
​
boolean addAll(Collection<? extends E> c); //批量添加
​
boolean addAll(int index, Collection<? extends E> c); //批量添加,指定开始的索引
​
boolean removeAll(Collection<?> c); //批量移除
​
boolean retainAll(Collection<?> c); //将c中不包含的元素移除
​
default void replaceAll(UnaryOperator<E> operator) {}//替换
​
default void sort(Comparator<? super E> c) {}// 排序
​
void clear();//清除所有的元素
​
boolean equals(Object o);//是否相等
​
int hashCode(); //计算获取hash值
​
E get(int index); //通过索引获取元素
​
E set(int index, E element);//修改元素
​
void add(int index, E element);//在指定位置插入元素
​
E remove(int index);//根据索引移除某个元素
​
int indexOf(Object o);  //根据对象获取索引
​
int lastIndexOf(Object o); //获取对象元素的最后一个元素
​
ListIterator<E> listIterator(); // 获取List迭代器
​
ListIterator<E> listIterator(int index); // 根据索引获取当前的位置的迭代器
​
List<E> subList(int fromIndex, int toIndex); //截取某一段数据
​
default Spliterator<E> spliterator(){} //获取可切分迭代器

值得一提的是里面出现了ListIterator,这是一个功能更加强大的迭代器,继承于Iterator,只能用于List类型的访问,拓展功能例如:通过调用listIterator()方法获得一个指向List开头的ListIterator,也可以调用listIterator(n)获取一个指定索引为n的元素的ListIterator,这是一个可以双向移动的迭代器。

操作数组索引的时候需要注意,由于List的实现类底层很多都是数组,所以索引越界会报错IndexOutOfBoundsException

ArrayList

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

继承了AbstractList接口,实现了List,随机访问,可克隆,序列化接口。线程不安全,如果需要线程安全适用其他类。低层是基于数组实现的,支持扩容。

成员变量

// 真正存取数据的数组
transient Object[] elementData; 
// 实际元素个数(不是elementData的大小,是具体存放的元素的数量)
private int size;

transient表示这个属性不需要自动序列化,因为element存储的不是真的元素的对象,而是指向对象的地址,所以这样的属性序列化是没有太大意义的。对地址序列化之后,反序列化的时候找不到之前的对象,所以需要手动实现对对象的序列化。这个需要我们看源码里面的readOject()writeOject()两个方法。其实就除了默认的序列化其他字段,这个elementData字段,还需要手动序列化和反序列化。

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // 序列之前需要保存原本的修改的次数,序列化的过程中不允许新修改
        int expectedModCount = modCount;
        // 将当前类的非静态和非transient的字段写到流中,其实就是默认的序列化
        s.defaultWriteObject();

        // 将大小写到输出流中
        s.writeInt(size);

        // 按照顺序序列化里面的每一个元素,注意使用的是`writeOject()`
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
        // 如果序列化期间有发生修改,就会抛出异常
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

    // 反序列化
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // 读取默认的反序列化数据
        s.defaultReadObject();

        // 读取大小
        s.readInt(); // ignored

        if (size > 0) {
            // 和clone()类似,根据size分配空间,而不是容量
            int capacity = calculateCapacity(elementData, size);
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // 循环读取每一个元素
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

这个方法在对象流中,通过反射进行调用。

ArrayList默认大小为10

private static final int DEFAULT_CAPACITY = 10;

定义了两个空数组,EMPTY_ELEMENTDATA名为空数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA名为默认大小空数组,用来区分是空构造函数还是带参数构造函数构造的arrayList,第一次添加元素的时候使用不同的扩容。之所以是一个空数组,不是null,是因为使用的时候我们需要制定参数的类型。

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

还有一个特殊的成员变量modCount,这是快速失败机制所需要的,也就是记录修改操作的次数,主要是迭代的时候,防止元素被修改。如果操作前后的修改次数对不上,那么有些操作就是非法的。transient表示这个属性不需要自动序列化。

protected transient int modCount = 0;

序列化id如下:为什么需要这个字段呢?这是因为如果没有显示声明这个字段,那么序列化的时候回自动生成一个序列化的id,这样子的话,假设序列化完成之后,往原来的类里面添加了一个字段,那么这个时候反序列化会失败,因为默认的序列化id已经改变了。假设我们给它指定了序列化id的话,就可以避免这种问题,只是增加的字段反序列化的时候是空的。

private static final long serialVersionUID = 8683452581122892189L;

构造方法

构造方法有三个,可以指定容量,指定初始的元素集,可以什么都不指定。

    // 指定初始化的大小
    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);
        }
    }

    // 什么都不指定,默认是空的元素集
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

   // 传入一个集合,转成数组之后,复制一份作为显得数据集
    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            // defend against c.toArray (incorrectly) not returning Object[]
            // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

常用方法

add

    public boolean add(E e) {
        // 确定容量是不是足够,足够就不会增加
        ensureCapacityInternal(size + 1); 
        // size+1的地方,赋值为现在的e
        elementData[size++] = e;
        return true;
    }
    public void add(int index, E element) {
        rangeCheckForAdd(index);// 检查位置是否合法
        modCount++;
        final int s;
        Object[] elementData;
        if ((s = size) == (elementData = this.elementData).length)
            elementData = grow();// 扩容
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = element;
        size = s + 1;
    }

get

    public E get(int index) {
        // 检查下标
        rangeCheck(index);

        return elementData(index);
    }
    // 检查下标
    private void rangeCheck(int index) {
        if (index >= size)
            // 数组越界
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }    

set

    public E set(int index, E element) {
        // 检查下标
        rangeCheck(index);
        // 获取旧的值
        E oldValue = elementData(index);
        // 修改元素
        elementData[index] = element;
        // 返回旧的值
        return oldValue;
    }

remove

    public E remove(int index) {
        // 检查下标
        rangeCheck(index);
        // 修改次数改变
        modCount++;
        // 获取旧的元素
        E oldValue = elementData(index);
        // 计算需要移动的下标(往前面移动一位)
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 调用native方法将后面的元素复制,移动往前一步
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 将之前的元素置为空,让垃圾回收方便进行
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    
    public boolean remove(Object o) {
        // 为空的元素
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            // 遍历,如果equals,则调用删除
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    // 快速删除方法
    private void fastRemove(int index) {
        // 修改次数增加1
        modCount++;
        // 计算移动的位置
        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 前面移动一位
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 置空
        elementData[--size] = null; // clear to let GC do its work
    }

扩容

add()方法里面去调用的。 在最小调用的时候容量不满足的时候,会调用grow()grow()是真正扩容的函数,每次扩容扩容为原来的1.5倍。

    private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

    private Object[] grow() {
        return grow(size + 1);
    }

    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);// 1.5倍扩容
        if (newCapacity - minCapacity <= 0) {// 超过int最大值
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE// 容量上线 20多亿 几乎不能达到
            : MAX_ARRAY_SIZE;
    }

LinkedList

LinkedList底层是双向链表实现的,还实现了Deque接口,继承AbstractSequentialList,AbstractSequentialList继承了AbstractList,也是线程不安全的

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

双向链表底层结构

    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 int size = 0;

    /**
     * Pointer to first node.
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     */
    transient Node<E> last;

构造函数

/**
 * Constructs an empty list.
 */
public LinkedList() {
}

/**
 * Constructs a list containing the elements of the specified
 * collection, in the order they are returned by the collection's
 * iterator.
 *
 * @param  c the collection whose elements are to be placed into this list
 * @throws NullPointerException if the specified collection is null
 */
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

添加

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

	// 往头部添加元素
    public void addFirst(E e) {
        linkFirst(e);
    }
    // 往尾部添加元素
    public void addLast(E e) {
        linkLast(e);
    }

add()方法默认是在尾部添加

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

可以在指定位置添加元素

    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

其中node(index)方法,当索引是在前面一半的时候,从前面开始遍历,当索引在后半部分的时候,从后面往前遍历

/**
 * Returns the (non-null) Node at the specified element index.
 */
Node<E> node(int index) {
    // assert isElementIndex(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;
    }
}

HashMap

HashMap使用动态数组+链表+红黑树构成,继承AbstractMap,实现了Map,Cloneable,Serializable

public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable

HashMap是线程不安全的,如果想使用线程安全的HashMap,可以通过Collection类的静态方法synchronizedMap获得线程安全的HashMap。

Map map = Collections.synchronizedMap(new HashMap());

HashMap的数据结构

HashMap通过key的hashCode计算hash值,hash值相同的挂载到一起,在相同hash值存储元素数量小于8时,使用链表存储,当元素数量大于8时,将链表转换为红黑树,当元素数量减少到小于6时,会再次由红黑树转为链表。

img

HashMap最上层是一个可扩容动态数组,考虑到扩容需要,该动态数组具有以下属性:

  • capacity:目前数组长度,值为2^n,每次扩容n会增加1,即数组容量变为之前的2倍,初始值为16

  • loadFactor:负载因子,默认值0.75,配合threshold使用

  • threshold:扩容阈值,等于capacity*loadFactor,当数组内元素达到这个阈值,就会触发扩容

初始化

HashMap初始化操作非常简单,就是确定initialCapacity,loadFactor的初始化值的过程。

平时我们调用无参构造函数时,都使用默认值。

也可以传入initialCapacity和loadFactor,进行自定义初始化

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}

但是,在进行初始化操作时,只是初始化了创建数组的相关参数,没有真正的创建动态数组,真正的动态数组的创建是在第一次进行数据写入时触发的。

数据写入

向HashMap中写入数据的过程

外部方法:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

内部插入方法:

/**
 * Implements Map.put and related methods.
 *
 * @param hash hash for key
 * @param key the key
 * @param value the value to put
 * @param onlyIfAbsent if true, don't change existing value
 * @param evict if false, the table is in creation mode.
 * @return previous value, or null if none
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 第一次插入数据,初始化,resize()方法也是扩容方法
    if ((tab = table) == null || (n = tab.length) == 0)
        // n为当前动态数组长度
        n = (tab = resize()).length;
    // 通过hash发现要放入的元素的数组位置为null,则直接把元素放在这里
    // p为赋值为当前动态数组目标位置的元素
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        // 要放入的位置已有元素
        Node<K,V> e; K k;
        // 判断目标位置第一个元素是否和新元素完全一致
        // 先判断hash,hash不一致则直接短路
        if (p.hash == hash &&
            // 再判断key是否一致
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        // 红黑树,调用红黑树插入方法
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // 列表,按照列表的方法插入
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    // 列表过长,转换为红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                // key相同就跳出循环
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        // 存在key,覆盖旧值
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        // 扩容
        resize();
    afterNodeInsertion(evict);
    return null;
}

扩容:

实际上是初始化和扩容

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    // 初始化oldCap设为0
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            // 旧容量大于最大容量(1<<30),扩容阈值设置为Integer最大值
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 // 容量扩大一倍并且小于最大容量,并且旧容量大于等于默认容量
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            // 扩容阈值翻倍
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        // 初始化容量被赋值扩容阈值
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        // 初始化使用默认数据
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    // 新阈值为0,loadFactor或初始化容量有误
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
    // 创建一个新数组
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        // 轮询,对所有元素进行重哈希
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    // 重哈希
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    // 树元素重哈希
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    // 链表元素重哈希
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

红黑树存放

final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                               int h, K k, V v) {
    Class<?> kc = null;
    boolean searched = false;
    TreeNode<K,V> root = (parent != null) ? root() : this;
    for (TreeNode<K,V> p = root;;) {
        int dir, ph; K pk;
        if ((ph = p.hash) > h)
            dir = -1;
        else if (ph < h)
            dir = 1;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {
            if (!searched) {
                TreeNode<K,V> q, ch;
                searched = true;
                if (((ch = p.left) != null &&
                     (q = ch.find(h, k, kc)) != null) ||
                    ((ch = p.right) != null &&
                     (q = ch.find(h, k, kc)) != null))
                    return q;
            }
            dir = tieBreakOrder(k, pk);
        }

        TreeNode<K,V> xp = p;
        if ((p = (dir <= 0) ? p.left : p.right) == null) {
            Node<K,V> xpn = xp.next;
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
            if (dir <= 0)
                xp.left = x;
            else
                xp.right = x;
            xp.next = x;
            x.parent = x.prev = xp;
            if (xpn != null)
                ((TreeNode<K,V>)xpn).prev = x;
            moveRootToFront(tab, balanceInsertion(root, x));
            return null;
        }
    }
}

链表转换为红黑树

final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        TreeNode<K,V> hd = null, tl = null;
        do {
            TreeNode<K,V> p = replacementTreeNode(e, null);
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}


评论