JDK源码阅读之 ArrayList。JDK版本1.8,

不同JDK版本ArrayList 容量扩充问题 ArrayList就是动态数组,也是一个对象。创建一个ArrayList对象,该对象存放在堆内存中,且是一个内存连续的内存区域。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝。 ArrayList的底层是由一个Object[]数组构成的,而这个Object[]数组,默认的长度是10,所以有的文章会说ArrayList长度容量为10。 不同Jd 阅读详情

ArrayList是我们最常用的集合框架,可以说没有之一,接下来阅读一下一下ArrayList的源码

简介

ArrayList继承自 AbstractList,实现了List,RandomAccess,Cloneable,Serializable(java.io)。JAVA类讲究见名知意。光看名字就能猜出来ArrayList底层是以数组实现的。所以ArrayList一定是有序的,但是存在重复数据。观看源码我们可以看出来,它是线程不安全的(因为通篇源码都没有synchronized和asynchronized关键字)

接下来放个截图,是ArrayList的所有方法和成员变量,内部类等


 

成员变量

serialVersionUID :

用来进行java序列化和反序列化的,没啥好说的(什么?你问我什么叫序列化?自己百度去,我特么怎么知道)

DEFAULT_CAPACITY:

我们看一下这个属性的定义

   private static final int DEFAULT_CAPACITY = 10;

作为一个合格的java码农(文档翻译官),我们解读一下这条属性的含义。我们都知道ArrayList的底层是个数组,我们还知道java集合框架内的元素数量是可变的,可是数组不可变,那么JDK在制作ArrayList的时候,一定要给他底层的数组(我们一会就能看到它)一个默认的长度,那么,这个属性的作用就是默认的长度。如果元素个数发生变化,那又是另一个故事了,以及当你使用一下代码

package com.study.basicJava.sourceCode;

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        System.out.println(list.size());
    }
}

运行的结果为0,那么又是另一个故事了。具体我们可以等到ArrayList的构造方法里面详细研究,我们暂且记下来这个属性

EMPTY_ELEMENTDATA

先看声明

private static final Object[] EMPTY_ELEMENTDATA = {};

注释翻译过来就是用来为空的ArrayList共享的数组,这句话说起来有点拗口,但是 我争取给大家解释清楚到底是怎么一回事。首先我们看这个EMPTY_ELEMENTDATA是用static关键字修饰的,那么说明,在整个ArrayList对象的生命周期内,它是一直不变的,也就是一直都是一个空数组。当你创造出一个空的ArrayList时,空的ArrayList用来存储元素的数组就是这个狗东西,但是一旦你向这个空的ArrayList中插入元素时,ArrayList就不使用它了。具体可以往后看。

DEFAULTCAPACITY_EMPTY_ELEMENTDATA

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

好,我们继续翻译注释。用来为确定大小的空的Arraylist实例共享的数组。这个数组的作用是用来和EMPTY_ELEMENTDATA区分当第一个元素被插入到ArrayList中时,我们应该为ArrayList扩充多大的空间。。。。。这个解释起来比较拗口,我们统一都放到构造方法里讲吧。大家就记住一点,当代码为

List<T> list = new ArrayList()<>

时,起作用的是EMPTY_ELEMENTDATA;、

当代码为

List<T> list =new ArrayList<>(10)

时,起作用的就是我们的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA

-----------------------------------------未完待续--------------------------------------------------------------------------------------------------------------------

elementData

代码先上

transient Object[] elementData;

在这行代码的后面,还有一句话,我觉得很有意思,也给大家写出来

// non-private to simplify nested class access

我们先看最后一句话,翻译过来就是,没有使用private修饰,以便简化嵌套类访问(很有意思,不是吗?)

先翻译一下注释:存储ArrayList元素的数组缓冲区,ArrayList的容量等于此数组的长度。任何一个空ArrayList都满足 elementData == DEFAULICAPACITY_EMPTY_ELEMENTDATA,当向这个空的ArrayList中插入第一个元素时,ArrayList的容量将会被扩充到DEFAULT_CAPACITY。

这一段话都很好理解,不好理解的讲到构造函数的时候也就好理解了,而我比较好奇的地方在于两个方面

1 为什么elementData没有用private之类的修饰符控制?

2 transient关键字的作用是什么?为什么要用transient关键字修饰?

因为本菜鸡是一边看源码,一遍写的这篇博客,所以,先记下来这两个问题,等下再研究。而且,我认为,特备是第二个问题,很有必要专门开一篇博客

size

代码

private int size;

一句话:当前ArrayList的容量,或者是当前ArrayList中元素的数量

现在进入构造函数部分

带有初始化容量的构造函数

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

注释里面写很清楚,构造一个指定容量的空ArrayList

参数 initialCapacity代表指定的容量大小,类型为int。需要注意的是,当参数initialCapacity为负数时,会抛出IllegalArgumentException

代码很好理解,大意就是当initialCapacity大于0时,用来存放元素的数组elementData被初始化,或者被赋值为一个长度为initialCapacity的Object数组。如果initialCapacity等于0的时候呢,elementData会被赋值为EMPTY_ELEMENTDATA。我们仍然记得,EMPTY_ELEMENTDATA是一个长度为0的Object静态static数组,为什么会将EMPTY_ELEMENTDATA赋值给elementData呢?我们可以这么理解,initialCapacity等于0,说明此时创造的ArrayList数组是个空数组,至少在此时,使用者没有发生或者没有现象预示即将发生对这个ArrayList的扩充行为(向ArrayList插入元素),我们用它来标识这个ArrayList是个空的,而且不会向里面插入元素

无参构造方法

 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

构造一个初始容量为10的ArrayList,但是里面没有元素;

在这个构造方法中并没有体现初始容量为10,只是将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData,标识此时的ArrayList虽然是空的,但是会插入元素。

参数为一个集合对象的构造函数

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

构造包含指定集合对象的元素的ArrayList,按集合对象的迭代器返回元素的顺序排列。

参数是指定的集合对象(Coolection对象都可以,但是如果ArrayList有泛型的话,只能放同样泛型的Coolection对象)

当指定的集合对象为null时,会抛出NPE异常

代码逻辑也很好理解,首先将指定集合对象C转换成数组赋值给elementData,再计算出此时ArrayList的容量,如果容量为0时,则将EMPTY_ELEMENTDATA赋值给elementData,如果容量不为0,那么此时还需要判断elementData是不是一个Object类型的数组,如果不是,就需要将elementData转换成Object数组。在这里,JDK使用了一个工具类Arrays的copy方法,这个方法的作用是将一个数组对象转换为指定大小,指定类型的数组对象,还是很有意思的,有机会可以好好研究一下

接下来就是ArrayList的各个方法了

trimToSize

public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

我们先不看具体这个方法是如何实现的,我想和大家一起,根据这个方法的名称去猜测这个方法到底是干啥的。trim,根据有道词典,意思为修剪,去除的意思。在java中呢,一般也是这个意思,比如,String就有一个trim()方法,作用是去除字符串内所有的空格。那么,我们大胆的猜测,trimToSize()的作用是将ArrayList的容量修剪为存储在当前ArrayList内拥有的元素数量。胡扯完毕,看注释

将ArrayList实例的容量缩减至ArrayList此时拥有的元素数量,应用程序可以使用此操作将ArrayList占有的内存得以最小化。

看来,我们才对了。

然后,我们看方法实现。这时候,我发现一个有意思的变量   modCount。我并没有在ArrayList的源码中看到这个变量的声明,看来,我们应该去ArrayList他爹,AbstractList中找找,不负众望,我找到这个鳖孙了

protected transient int modCount = 0;

这么长的一段话呢,看了就头皮发麻,咱们主要的目的就研究ArrayList,不是研究他爹,我们说一说这个属性是干嘛的就行了。

这个属性在ArrayList中的作用主要是为了记录ArrayList中存储的元素增删改的次数,也可以说是元素数量变化的次数。当然,他还一个别的作用,我们暂时研究不到这个地步,不过说一说也无妨。根据注释解释的来看,他还记录了一种状况,那就是当ArrayList使用迭代器的时候,由于某些操作,使迭代后的结果和我们期望的结果不同。说白了,就当多线程同时工作时引起的ArrayList(所有继承了abstractlist的类都会有这种情况)数据不同步。这些是一些线程安全问题,暂时先不讨论

接下来的这个方法是用来保证或者说扩充ArrayList的容量的

    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

方法内先计算最小扩充量,以保证能够放下参数minCapacity所代表的容量。计算规则比较简单。首先要看存储数据的elementData是否是一个空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),如果是的话,最小扩充量就为0.如果不是的,最小扩充量就是10(DEFAULT_CAPACITY)

之后在比较一下最小扩充量和参数的大小。如果参数大于我们计算出的最下扩充量,说明我们确实需要扩容,因此,调用另一个方法

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

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

既然选择扩容了,那么集合内的元素数量是肯定会发生变化,因为modCount需要自增。之后开始条件判断,如果参数大于此时集合内部元素的数量,那么再次调佣另一个方法

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

这个方法才是真正保证集合的容量能够大于等于参数。先记录之前的元素数量,接下来我们看到一个移位运算,且不管这个移位运算最终的结果如何,但是我们会发现,新的容量一定是大于旧的容量的。接下来的逻辑就简单了。如果新容量小于参数,那么就将参数赋值给新容量。

然后重置elementData。

还有几个方法也是用来计算容量相关的,我们来看一下

容量计算

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

逻辑很好理解,这里不再赘述。

接下来这个方法的意义没看太懂,先记录一下

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

返回集合内元素的数量

    public int size() {
        return size;
    }

判断集合内是否有元素

public boolean isEmpty() {
        return size == 0;
    }

判断集合内是否含有参数元素

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

集合拷贝方法(浅拷贝,不会复制元素本身)

    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

浅拷贝最大的特点就是,假设我们的源集合对象是list,复制得到的集合对象是copyList,那么

list.equals(copyList)

的返回值为false。

数组转换方法

    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

这个方法按照JDK注释来讲,其实是数组和集合之间的桥梁,允许集合转数组。

在ArrayList中,这个方法的返回值是一个新分配的数组,它包含ArrayList内的所有元素,元素顺序和ArrayList中的元素顺序一致。而且,由于返回值是新分配的,所以我们可以对这个数组做出更改操作(添加或移除元素)

另一个带泛型的集合转数组方法(推荐使用)

这个方法带一个入参,就是一个泛型数组。这个方法的精妙之处在于返回数组的处理。如果参数a的长度比集合内元素的数量小的话,返回值是一个长度为集合元素数量的泛型数组。反之则会将集合内的元素填充到数组a中,然后a剩下的位置将会以null填充。

    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

根据下标获取元素的一个方法。由于缺省了修饰符,所以这个方法只能在本类和同一包下的其他类内使用。

众所周知,我们的ArrayList是以数组为底层实现的,那么获取某个位置的元素,其实也就是获取elementData数组中某个位置的元素。

E elementData(int index) {
        return (E) elementData[index];
    }

接下来是暴露给外部的获取元素的方式,参数也是下标。当然,首先还是要检查一下元素下标是否超过元素数量

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

元素替换

    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

在集合尾部添加元素。首先先对集合进行扩容,也就是说得判断elementData的长度是否还够用,不够用就要重新申请一个新数组,具体逻辑就在前面。

    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(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

移除集合中某个位置的元素

    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        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

        return oldValue;
    }

真正的原理就是从让这个位置往后的元素统一向前移动一位,然后申请一个新的数组盛放新的元素。我们需要注意的是移除元素的方法会先检查参数的合法性,并且会返回被删除的元素。

还有一个快速删除的方法,原理都一样,但是不会检查参数和返回删除元素

   private void fastRemove(int index) {
        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
    }

这是一个私有方法,具体作用在于我们接下来要介绍的一个方法,删除特定元素

    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++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

原理和contains类似,如果集合内有这个元素就移除,没有就返回false。

清除集合

    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

将某个集合被的全部元素添加到ArrayList中,第一个参数是指插入的位置

    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

逻辑也很好理解,仍然是重新分配数组,利用toArray方法将参数集合C转换为数组然后与elementData合并。

移除集合内一段区间内的元素

protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

我们注意到这个方法是使用protected修饰的,说明它只在本类和同包下使用。

首先对modCount进行自增操作,表示集合内的元素数量发生了改变;然后计算需要移除的元素数量。在之前,我们说过System.arraycopy方法的含义,在这里我们重新说一下

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

从src数组中下标为srcPos的位置开始,向后复制length个元素,然后从dest数组的下标为destPos位置开始粘贴。

继续来看我们的方法。那么这个操作的含义就是将elementData中从下标为toIndex位置开始复制numMoved个元素,然后从elementData的下标为fromIndex的位置开始粘贴。

计算出移除后集合元素的新数量newSize(总数量减去移除的数量就是剩下的数量),然后将移除过的位置统一赋值为null,通知GC回收

移除交集

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }

移除参数C集合内的所有元素,但是有个要求,集合C不能为null。集合C可以是当前ArrayList的子集,也可以不是,甚至可以没有交集。

但是我们可以根据返回值看出来。如果当前集合和参数c有交集就会返回true,然后交集会在当前集合中被移除;否则返回false。

移除差集

    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);
    }

按照注释的说法是只保留集合内与C相同的元素,说白了就是保留交集,移除差集。

如果当前List不是集合C的一个子集,则返回true。否则返回false。

或者调用此方法后,当前集合发生了改变就返回true,反之false。

批量删除

    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

刚才的removeAll和retainAll的具体实现都是靠这个方法。通过传递complement来决定到底移除那些元素,删除方式和removeRange方法基本一样

接下来看一看ArrayList的几个内部类。

第一个,Itr,据说是AbstractList.Itr的优化版

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

基本和AbstractList.Itr一样唯一不同的是,AbstarctList.Itr获取数据用的是get,但是在ArrayList中进行了更确切的实现,就是直接获取数组中的元素。

至此,ArrayList基本的东西都扯完了。由于本楼主也是个菜鸟,这篇博客其实就是把JDK文档翻译了一遍,一些难懂的知识基本也没设计(序列化等),有什么不对的地方希望大家给我指出来,谢谢

Java集合类框架学习 2 —— ArrayList(JDK1.8/JDK1.7/JDK1.6) 这一篇开始说ArrayList 参考代码为jdk1.6_45 jdk1.7_80 jdk1.8_111中的源码,对比阅读,发现修改的问题以及改进点 public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable 一、基本性质 阅读详情

相关推荐

基于反相器的超声波发生和气泡检测电路

本文主要介绍了关于超声波的两种电路——基于反相器的振荡电路和基于逻辑们的气泡检测电路

xehagy的博客 1954

深入Java集合:ArrayList 源码解析 - JDK8

深入解析Java集合:ArrayList和Vector源码解析,超详细

weixin_51612062的博客 1143

手把手教你用TLP350驱动IGBT:光耦隔离驱动电路完整设计教程

本文详细介绍了如何使用TLP350光耦隔离驱动电路设计IGBT驱动方案。从TLP350芯片特性分析到驱动电路架构设计,包括信号调理、自举电源和栅极控制网络,再到PCB布局优化和保护电路设计,提供了完整的实战指南。通过实测波形分析和故障排查,帮助工程师快速掌握IGBT驱动电路的设计要点和进阶技巧。

weixin_30566063的博客 822

lists.newarraylist()和new arraylist() 区别

List<Integer> list = new ArrayList<Integer>(); List<Integer> list = Lists.newArrayList(); Lists和Maps是两个工具类, Lists.newArrayList()其实和new ArrayList()几乎一模一样, 唯一它帮你做的(其实是javac帮你做的),就是自动...

紫蝶侠的博客 2357

秦时明月6.2魔改版_从零到一部署指南_含安卓客户端配置与GM后台直通

本文详细介绍了秦时明月6.2魔改版的从零到一部署指南,包括环境准备、服务端安装、安卓客户端配置以及GM后台直通等关键步骤。特别针对安卓客户端配置和GM工具使用提供了实用技巧,帮助用户快速搭建并优化游戏服务器,确保稳定运行。

weixin_42522964的博客 315

避坑指南:Vue调用摄像头常见6大报错解决方案(navigator.mediaDevices全兼容方案)

本文深入剖析了在Vue应用中调用摄像头时常见的六大报错,如权限拒绝、设备未找到等,并提供了基于navigator.mediaDevices的全兼容解决方案。内容涵盖从HTTPS环境配置、浏览器兼容性处理到摄像头资源管理与高级错误处理,旨在帮助开发者构建稳定可靠的企业级摄像头应用。

Black的专栏 337

JDK源码分析--ArrayList深入理解

一、实现原理 以JDK1.8.0_74源码为基础进行分析。 1、基于数组的实现,是一个容量能自动增长的动态数组。 2、ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。 3、随着向ArrayList中不断添加元素,其容量...

WolfShadow的博客 1万+

瑞芯微RK3576开发实战:固件烧录与内核启动的典型问题解析

本文详细解析了瑞芯微RK3576开发板在固件烧录与内核启动过程中遇到的典型问题及解决方案。从开发工具版本检查、DDR配置调整到内核启动异常的设备树适配和电源管理问题,提供了系统性的调试方法和实用技巧,帮助开发者高效解决RK3576开发中的常见挑战。

weixin_33700350的博客 454

jdk源码——集合(ArrayList)

写了一篇文章,jdk源码——集合(一)

博客:想飞的猪 346

【实战指南】Green Hills MULTI-IDE 从零安装到工程创建全流程

本文详细介绍了Green Hills MULTI-IDE从零安装到工程创建的全流程,包括系统要求检查、安装步骤、工程配置及调试技巧。特别适合嵌入式开发新手快速上手这款专业IDE工具,提升开发效率。

weixin_28740299的博客 474

DNF单机版搭建全攻略:从零开始到局域网联机(含常见问题解决)

本文提供详细的DNF单机版搭建指南,从环境配置到局域网联机设置,涵盖服务端安装、数据库配置及常见问题解决方案。适合新手和有经验的玩家,帮助快速搭建专属游戏服务器,重温经典游戏体验。

weixin_30832143的博客 844

ArrayList(JDK1.8)源码解析

既然是看源码,那我们要怎么看一个类的源码呢?这里我推荐的方法是: 1)看继承结构 ​ 看这个类的层次结构,处于一个什么位置,可以在自己心里有个大概的了解。 2)看构造方法 ​ 在构造方法中,看做了哪些事情,跟踪方法中里面的方法。 3)看常用的方法 ​ 跟构造方法一样,这个方法实现功能是如何实现的 注:既然是源码,为什么要这样设计类,有这样的继承关系。这就要说到设计模式的问题了。所以我们要了解常用的...

ThinkWon的博客 1万+

YOLOv13改进策略【Backbone/轻量化】| 替换骨干网络 CVPR-2024 StarNet,超级精简高效的轻量化模块

本文记录的是。StarNet设计简洁,没有复杂的设计和精细调整的超参数,仅是一个 4 阶段的分层架构。并且其中能够在低维空间计算的同时考虑极高维的特征这一特性,进而提高模型精度。本文在替换骨干网络中配置了原论文中的starnet_s1starnet_s2starnet_s3和starnet_s4七种模型,以满足不同的需求。

Limiiiing的博客 669

JDK 1.8 ArrayList类源码阅读记录

JDK 1.8 ArrayList类源码阅读记录 JDK 1.8 ArrayList类源码阅读记录JDK 1.8 ArrayList类源码阅读记录一、简介二、属性三、构造方法四、ArrayList扩容机制五、方法5.1 增add(E e)****add(int index, E element)********addAll(Collection<? extends E> c)********addAll(int index, Collection<? extends E> c)***

nous_x的博客 1355

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

3.jdk源码阅读之ArrayList(上)

我个人认为看代码不应该流水账一般的看,而应该带着问题去看。换个说法就是你想通过阅读源码解答你心中什么疑问?回到jdk源码,ArrayList应该是java程序员日常开发中用的比较多的类。有以下几个问题,我觉得可以从源码中找到答案:1.当向ArrayList中添加元素的时候,如果容量不够了,它是怎么扩容的?什么时候扩容?扩到多大?2.我们都知道ArrayList线程不安全,那是什么导致的线程不安全呢?3.ArrayList底层是通过什么数据结构来实现的?

前端、移动端、后端源码级底层原理分享 1216

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

JDK8和JDK17的ArrayList 源码对比分析

JDK 8 和 JDK 17 的扩容机制并不是绝对的1.5倍扩容,两者都允许根据实际需要动态调整容量。JDK 17 的实现通过更灵活的扩容策略和更清晰的代码结构,提升了性能和可维护性,JDK 8扩容逻辑较为简单,分为几个步骤,JDK17的逻辑更为集中,减少了代码的复杂性。 个人去测试过JDK 8 和 JDK 17 中ArrayList添加数据的一个表现情况,JDK17中ArrayList的效率大概会比JDK 8有5%左右的提升。

xweiran的博客 1075

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ArrayList源码阅读

本节目标:阅读面试中常问的 ArrayList 八股对应的源码,了解其背后的代码逻辑注意,本次阅读的源码是版本。

m0_57877510的博客 719

JDK源码——ArrayList(JDK8)

文章目录JDK源码——ArrayList(JDK8)1.类的定义2.常量4.成员变量5.构造方法6.普通方法——扩容6.1检查6.2扩容7.普通方法——写入8.普通方法——写出9.普通方法——删除10.普通方法——序列化11.迭代器11.1 Itr11.1.1声明11.1.2成员变量11.1.3方法11.2ListItr11.2.1声明11.2.2构造方法11.2.3普通方法11.3获取迭代器12.其他方法 JDK源码——ArrayList(JDK8) ArrayList,一个可扩容的,底层用数组实现的,非

MoozLee的博客 608

JDK1.8源码阅读之java.util.ArrayList

JDK1.8源码阅读之java.util.ArrayList 构造器方法 无参构造器 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData; // non-private to simplify nested class access // 将elementData数组初始化为空。 // 这里是惰性扩容,当要往数组中插入元素时,才会初始化扩容成默认长度10。

striver 13 227

ArrayList源码逐句解析!(添加元素,add方法以及其重载方法,扩容机制grow)以及面试题(基于JDK1.8)

上期我们已经逐句的解析了Arraylist的底层数据结构以及它的构造方法,不太熟悉的读者可以再去温故。本期我们将在其基础上,进一步阅读关于ArrayList的添加元素、删除元素以及其扩容机制的代码,希望能让读者有所收获,也希望各位读者对于本文的小错误能不吝赐教,码海无涯,愿与大家共勉。

weixin_52469927的博客 1995

ArrayList源码阅读记录(JDK 1.8)

ArrayList源码阅读记录(JDK 1.8)类继承结构ArrayList中存储的数据结构构造方法添加元素删除元素查询元素修改元素 类继承结构 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.i...

子謀的博客 245

我是如何阅读JDK源码的?

1. 前言 之前断断续续读过一部分 JDK 常用类的源码,这里想把过程中的一些心得和方法记录下来,如果能帮到需要的小伙伴就再好不过了!本文主要分享一下我的阅读工具和阅读顺序。PS: 由于...

WriteOnRead 1192

JDK源码阅读顺序

很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起。以下为小编整理的通常所需阅读的源码范围。 标题为包名,后面序号为优先级1-4,优先级递减 1、java.lang 1) Object 1 2) String 1 3) AbstractStringBuilder 1 4) StringBuffer 1 5) StringBuilder 1 6) Boolean 2 7) B...

猴子哥哥的博客 6万+

JDK1.8源码阅读记录ArrayList类

JDK1.8源码阅读记录 JAVA.Util包 ArrayList类 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable ArrayList类继承AbstractLi...

weixin_43459704的博客 167

JDK源码阅读篇——持续更新

默认扩容为旧容量的 1.5 倍;若 1.5 倍仍不够,直接用所需最小容量;若超过最大限制,用或抛出 OOM;最后通过数组拷贝完成扩容。LinkedList 通过Node的prevnext指针实现双向链表,通过firstlast指针快速定位头尾,实现高效增删。增删快:仅修改指针,无需移动元素(已知节点时 O(1))。查询慢:无下标,需遍历节点(O(n))。要不要我帮你整理一份JDK 核心类源码阅读重点笔记?笔记会提炼每个类的核心逻辑、关键方法和面试高频考点,方便你快速复习和查阅。

weixin_44840369的博客 1990

【JDK1.8源码阅读】ArrayList.class(完)

//List.java public interface List<E> extends Collection<E> //Collection.java public interface Collection<E> extends Iterable<E> //Iterable.java public interface Iterable<T...

大猴子 380
上一篇: Ext.Data.Store的loadData方法注意事项
下一篇: JDK源码阅读——集合框架——Collection接口
吸烟喝可乐
博客等级 码龄11年 10粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值