在我们的面试中ArrayList也是一个很基础的知识点,本文就带你一起学习ArrayList的部分源码。如果有不正确的地方,欢迎指正。祝你学习愉快。

由于网上大部分的解析都是基于JDK8的,然而我的JDK是11。所以我就来写一篇ArrayList在JDK11中的源码剖析。如果8的和11的变化较大,我还会写关于8和11的区别

源码部分较多,一次读不完,可以收藏了下次再读

对应的知识点总结:
ArrayList原理讲解

文章目录

  • 1、ArrayList类的继承关系
    • 1.1、ArrayList对应的类图
    • 1.2、AbstractList类
    • 1.3、RandomAccess接口类
    • 1.4、Cloneable接口类
    • 1.5、Serializable接口类
  • 2、ArrayList成员变量
  • 3、ArrayList构造方法
    • 3.1、空参构造
    • 3.2、指定容量的带参构造
    • 3.3、以集合为参数的构造方法
  • 4、ArrayList常用成员方法
    • 4.1、传入指定元素的add()(第一次)
    • 4.2、传入指定位置和元素的add()
    • 4.3、传入的参数为一个ArrayList对象
    • 4.4、传入的参数为一个ArrayList对象,且制定了具体位置
    • 4.5、根据索引对元素进行删除
    • 4.6、根据指定元素进行删除
    • 4.7、修改集合元素
    • 4.8、获取指定的元素
    • 4.9、toString方法
    • 4.10、获取迭代器的方法

1、ArrayList类的继承关系

1.1、ArrayList对应的类图

其中ArrayList继承了List接口,AbstractList也继承了List接口,这一点历史,我在HashMap的源码分析中,已经阐述过,这里就不再重复。

我们发现ArrayList继承了AbstractList,并且实现了三个不同的接口RandomAccess、Cloneable、Serializable

1.2、AbstractList类

AbstractList类为ArrayList的直接父类,也是最终要的父类,待会在源码分析中我们会进行重点讲解

1.3、RandomAccess接口类

RandomAccess接口是一个标记接口,用以标记实现的List集合具备快速随机访问的能力。

public interface RandomAccess {}

在我们的数据访问中有随机访问顺序访问两种:

随机访问(通过索引访问)

for (int i=0, n=list.size(){i < n; i++) list.get(i);
}

顺序访问(迭代器访问)

for (Iterator i=list.iterator(); i.hasNext(); ){i.next();
}

在数据访问的集和ArrayList和LinkedList中,他们分别在不同的访问方式上占优。ArrayList随机访问更快,LinkedList顺序访问更快。而RandomAccess接口的作用就是为了区分这两个集和,以此能够让我们在实际业务代码中到底使用那种访问方式。例如:

if (list instanceof RandomAccess) {for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}
} else {for (User user : list) {System.out.println(user);}
}

LinkList与ArrayList的区别:LinkList是一个基于双向链表的数据结构,ArrayList是一个基于动态数组的数据结构

LinkList的随机访问数据源码:

//每次LinkedList对象调用get方法获取元素,都会执行以下代码
list.get(i);public E get(int index) {//判定输入的index节点是否有效(方法详情,请查看补充方法一、二)checkElementIndex(index);//执行node()方法,返回对应的数据(方法详情,请查看补充方法三)return node(index).item;
}

补充方法一:

//补充方法一
private void checkElementIndex(int index) {//如果不是正常范围的索引,就抛出异常if (!isElementIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

补充方法二:

//补充方法二
private boolean isElementIndex(int index) {//如果传入的index节点大于等于0并且小于链表长度就返回truereturn index >= 0 && index < size;
}

补充方法三:

//补充方法三
Node<E> node(int index) {//将链表的总长度右移以为(长度/2)再和需要查找的索引进行比较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;}
}

LinkList的顺序访问数据源码:(顺序有点乱,建议耐心看完)

//使用迭代器遍历该链表,首先需要创建一个迭代器(具体方法详情,查看补充方法四)
Iterator<String> iterator = strings.iterator();
//判断下一个元素是否为空(return nextIndex < size)
while (iterator.hasNext()){//依次遍历链表节点(具体方法详情,查看补充方法六)s = iterator.next();//打印遍历出来的数据即可System.out.println(s);
}

补充方法四:

//调用迭代器,返回listIterator方法
public Iterator<E> iterator() {return listIterator();
}
//调用listIterator方法,返回listIterator()方法
public ListIterator<E> listIterator() {return listIterator(0);
}
public ListIterator<E> listIterator(int index) {//用于检测索引是否符合规范(具体方法详情,查看补充方法五)checkPositionIndex(index);//实例化一个带参的ListItr对象,得到一个存放链表的集合(我在调试的时候,发现这个对象会做很多乱七八糟的工作,都跑到计算hash去了,具体的我就不太懂了)return new ListItr(index);
}

补充方法五:

private void checkPositionIndex(int index) {//如果不是正常范围的值,就抛出异常if (!isPositionIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {//如果传入的index节点大于等于0并且小于数组长度就返回truereturn index >= 0 && index <= size;
}

补充方法六:一定要区别于ArrayList对应迭代器的next方法

//该方法来自于AbstractList类的内部类ListItr下的方法
public E next() {//判断是否会发生并发修改异常checkForComodification();//判断下一个index节点是否小于链表长度,不小于则抛出异常if (!hasNext())throw new NoSuchElementException();//进行相应的赋值操作lastReturned = next;next = next.next;nextIndex++;return lastReturned.item;
}

由于随机访问的时候源码底层每次都需要进行折半的动作,再经过判断是从头还是从尾部一个个寻找。而顺序访问只会在获取迭代器的时候进行一次折半的动作,接下来获取元素都是在上一次的基础上获取下一个元素。因此顺序访问要比随机访问快得多

1.4、Cloneable接口类

一个类实现 Cloneable 接口来指示 Object.clone() 方法,该方法用于该类的实例进行字段的复制。在不实现 Cloneable 接口的实例上调用对象的克隆方法会导致 CloneNotSupportedException 异常。简言之克隆就是依据已经有的数据,创造一份新的完全一样的数据拷贝。

具体的运用(浅克隆、深克隆)可以查看之前的博客,原型模式

1.5、Serializable接口类

序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。 可序列化类的所有子类型都是可序列化的。 序列化接口没有方法或字段,类似于RandomAccess类

序列化:将对象的数据写入到文件(写对象)

反序列化:将文件中对象的数据读取出来(读对象)


2、ArrayList成员变量

明确并且牢记成员变量的含义,有助于对成员方法的理解

1、序列化版本号

private static final long serialVersionUID = 8683452581122892189L;

2、default_capacity(数组默认的容量)

private static final int DEFAULT_CAPACITY = 10;

3、empty_elementdata(用于空实例的共享空数组)

private static final Object[] EMPTY_ELEMENTDATA = {};

4、defaultcapacity_empty_elementdata(默认容量的空数组)

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

5、集合真正存储数组元素的数组

transient Object[] elementData;

6、集合的大小

private int size;

3、ArrayList构造方法

构造方法 方法详情
ArrayList<>() 无参构造,默认创建一个初始容量为十的空列表
ArrayList<>(int initialCapacity) 带参构造,创建一个指定初始容量的空列表
ArrayList<>(Collection<? extends E>c) 带参构造,创建一个包含指定集合的元素的列表

3.1、空参构造

初始化的集合是空集合

public ArrayList() {//由于没有指定集合大小,所以采用默认集合容量(空集合)进行赋值this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

3.2、指定容量的带参构造

根据参数创建指定大小的结合

public ArrayList(int initialCapacity) {if (initialCapacity > 0) {//如果指定集合容量大于0,跳转到Object类,进行创建指定大小的数组this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {//如果指定的容量是0,则创建一个空实例的共享空数组(这里的空数组不同于空参构造创建的空数组)this.elementData = EMPTY_ELEMENTDATA;} else {//除此之外,抛出异常,比如负数throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}
}

3.3、以集合为参数的构造方法

将原集合的数据更新到新集合上

public ArrayList(Collection<? extends E> c) {//将集合构造方法中的集合对象转成数组,且将数组的地址赋值给elementData(真正存储数组元素的数组),toArray底层也是调用了补充方法七的方法elementData = c.toArray();//如果原集合的长度不等于0,就循环if ((size = elementData.length) != 0) {//判断elementData和Object[]是否为一样的类型if (elementData.getClass() != Object[].class)//如果不一样,就使用Arrays的copyOf方法进行元素的拷贝(具体方法详情,查看补充方法七)//三个参数分别为:原集合,原集合长度,Object类型elementData = Arrays.copyOf(elementData, size, Object[].class);} else {//如果原集合的长度等于0,就将空数组集合进行赋值this.elementData = EMPTY_ELEMENTDATA;}
}

补充方法七:copyOf(将原集合拷贝到一个新的Object类型并且长度和原长度相同)

//三个参数分别为:原集合,原集合长度,Object类型
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {//使用镇压警告@SuppressWarnings("unchecked")//使用三元运算符进行赋值//判断传入的类型是否与Object类型相同T[] copy = ((Object)newType == (Object)Object[].class)//相同就返回一个新的Object类,并指定原来集合的长度? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);//将数组的内容拷贝到 copy 该数组中,arraycopy底层是native方法System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));//返回拷贝元素成功后的数组return copy;
}

4、ArrayList常用成员方法

4.1、传入指定元素的add()(第一次)

不指定集合容量,使用系统默认的10

public boolean add(E e) {//修改次数,该成员变量来自AbstractList类,默认初始值为0modCount++;//调用add方法,参数分别为:集合类型的数据,集合真正存储数据的集合,集合的大小add(e, elementData, size);//增加成功,返回一个true的布尔值return true;
}
//参数分别为:集合类型的数据,集合真正存储数据的集合,集合的大小
private void add(E e, Object[] elementData, int s) {if (s == elementData.length)//如果集合的大小等于集合整整存储数据的集合大小,就进行扩容//如果我们使用的是无参构造,集合初始容量为默认的空基和,即长度为0.size默认也是0elementData = grow();//在返回的容量为10的数组中s位置,添加我们的传入的e元素elementData[s] = e;//集合的大小+1size = s + 1;
}
//调用具体的扩容方法
private Object[] grow() {//将集合的容量大小+1return grow(size + 1);
}
private Object[] grow(int minCapacity) {//此处的copyOf方法前文已经讲解过(注意参数含义的变化)//利用传入的参数,复制出一个新的数组,再赋值给真实的存储数据的集合(具体方法详情,查看补充方法八)return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));
}

补充方法八:

//传入的参数为,size+1,即集合容量+1
private int newCapacity(int minCapacity) {//将原来真实集合的长度赋值给oldCapacityint oldCapacity = elementData.length;//将原集合的长度扩大1.5倍,再赋值给新的集合长度(第一次进来的时候为:0*1.5=0)int newCapacity = oldCapacity + (oldCapacity >> 1);//如果新的集合长度-旧的集合长度小于等于0if (newCapacity - minCapacity <= 0) {//判断集合的容量与默认的空数组集合是否相等if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)//返回传入参数和系统默认容量中的最大的值(第一次添加,最小容量为1,所以返回系统默认的容量)return Math.max(DEFAULT_CAPACITY, minCapacity);//如果传入的容量小于0,就抛出异常if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return minCapacity;}//如果新的集合长度-旧的集合长度没有小于等于0 ,那就进行对应的运算//新的集合长度是否小于等于集合的最大长度。如果是就返回新的集合长度,如果不是,就返回集合的最大值或Integer的最大值(具体方法详情,查看补充方法九)return (newCapacity - MAX_ARRAY_SIZE <= 0)? newCapacity: hugeCapacity(minCapacity);
}

补充方法九:

private static int hugeCapacity(int minCapacity) {//判断传入参数是否小于0,是就抛出异常if (minCapacity < 0) // overflowthrow new OutOfMemoryError();//如果传入的参数大于集合的最大容量,就返回Integer的最大值,否则就返回集合的最大值return (minCapacity > MAX_ARRAY_SIZE)? Integer.MAX_VALUE: MAX_ARRAY_SIZE;
}

一般的第二次添加数据时,不进行扩容直接进行赋值

private void add(E e, Object[] elementData, int s) {//此处的判断不成立,不进行扩容if (s == elementData.length)elementData = grow();//将需要添加的元素(此处传入的元素e)赋值在集合的s位置(s即为上一次的size)elementData[s] = e;size = s + 1;
}

集合在第一次添加数据的时候,系统会默认给集合10个空间的集合容量;

如果集合容量大于10以后,再进行扩容就是扩大为之前的1.5倍

4.2、传入指定位置和元素的add()

集合太小就扩容后添加到新的集合,否则直接添加

public void add(int index, E element) {//对传入的index值进行校验,如果大于集合长度或者小于0九抛出异常rangeCheckForAdd(index);//修改次数+1modCount++;final int s;//新建一个Object类型的数组Object[] elementData;//s = size 将集合末尾位置的下标值(不是集合的容量)赋值给s//(elementData = this.elementData).length 将我们的集合赋值给新创建的集合,再获取集合的长度//如果集合末尾位置的下标值与我们集合的长度相等,那么我们就进行扩容if ((s = size) == (elementData = this.elementData).length)elementData = grow();//arraycopy底层使用了native关键字,调用方法将elementData数组的元素拷贝到新的elementData数组中System.arraycopy(elementData, index,elementData, index + 1, s - index);elementData[index] = element;//将集合大小+1size = s + 1;
}

4.3、传入的参数为一个ArrayList对象

集合容量比较后,进行相应的赋值

public boolean addAll(Collection<? extends E> c) {//调用toArray方法,调用底层的copyOf方法(该方法前文已经讲解了,补充方法七)//copyOf(将原集合拷贝到一个新的Object类型并且长度和原长度相同)Object[] a = c.toArray();//集合的修改次+1modCount++;//获取传入参数集合的长度,赋值给numNew参数int numNew = a.length;if (numNew == 0)//如果传入参数的为0则返回falsereturn false;//新建一个Object数组Object[] elementData;final int s;//(elementData = this.elementData).length 将我们的集合赋值给新创建的集合,再获取集合的容量//如果传入集合的长度 > 我们集合的长度相等,那么我们就进行相应的扩容,扩容的长度if (numNew > (elementData = this.elementData).length - (s = size))elementData = grow(s + numNew);//arraycopy底层使用了native关键字,调用方法将a数组的元素拷贝到elementData数组中System.arraycopy(a, 0, elementData, s, numNew);//新的集合大小 = 传入的集合大小 + 之前的集合大小size = s + numNew;//方法执行成功,返回truereturn true;
}

4.4、传入的参数为一个ArrayList对象,且制定了具体位置

public boolean addAll(int index, Collection<? extends E> c) {//校验索引的正确性rangeCheckForAdd(index);//将旧集合中的元素拷贝到新的集合中Object[] a = c.toArray();//修改次数+1modCount++;//获取新集合的长度,再进行赋值int numNew = a.length;if (numNew == 0)//如果新集合的长度为0,即传入的集合为null,就结束方法,返回falsereturn false;//创建一个存储元素的集合Object[] elementData;final int s;//(elementData = this.elementData).length 被添加的集合容量//如果传入参数的集合(添加的集合) > 被添加的集合容量 - 集合长度大小,就行行扩容if (numNew > (elementData = this.elementData).length - (s = size))elementData = grow(s + numNew);//计算移动的元素numint numMoved = s - index;if (numMoved > 0)//如果需要移动的元素大于0,则进行对应的//arraycopy底层使用了native关键字,调用方法将elementData数组的元素拷贝到elementData数组中System.arraycopy(elementData, index,elementData, index + numNew,numMoved);//如果不需要移动,就直接根据参数将a数组的元素拷贝到elementData数组中System.arraycopy(a, 0, elementData, index, numNew);//集合的大小进行相应的添加size = s + numNew;//方法执行成功,返回truereturn true;
}

对于add()方法,重点学习我们的传入指定元素方法,后面的就一通百通了,因为很多的方法、步骤都相同

4.5、根据索引对元素进行删除

找到对应索引,赋值为null,让垃圾回收器回收

public E remove(int index) {//对传入的索引值进行校验(方法详情,请查看补充方法十)Objects.checkIndex(index, size);//将集合真正存储元素的数组赋值给一个Object类型的数组final Object[] es = elementData;//镇压警告//将对应索引的元素赋值给oldValue,方便后期返回@SuppressWarnings("unchecked") E oldValue = (E) es[index];//根据索引和新创建的数组集合(数据来自原集合)删除指定位置的元素(方法详情,请查看补充方法十一)fastRemove(es, index);//返回被移除的元素的元素return oldValue;
}

补充方法十:

public static int checkIndex(int index, int length) {return Preconditions.checkIndex(index, length, null);
}
//正真校验的方法体
public static <X extends RuntimeException> int checkIndex(int index, int length,BiFunction<String, List<Integer>, X> oobef) {if (index < 0 || index >= length)//如果索引值小于0,或者索引值大于集合的大小就抛出异常throw outOfBoundsCheckIndex(oobef, index, length);return index;
}

补充方法十一:

private void fastRemove(Object[] es, int i) {//修改次数+1modCount++;final int newSize;//size - 1 数组的下标为0开始,-1运算后表示与数组下标同步//如果集合的数据容量 > 我们指定的索引,即为查找到了需要移除的元素if ((newSize = size - 1) > i)System.arraycopy(es, i + 1, es, i, newSize - i);//将原集合删除元素的位 置为null,尽早让垃圾回收机制对其进行回收es[size = newSize] = null;
}

4.6、根据指定元素进行删除

根据元素,找到对饮的索引,再使用同4.5一样的删除方式

public boolean remove(Object o) {//将集合真正存储元素的数组赋值给一个Object类型的数组final Object[] es = elementData;//获取调用对象集合的大小final int size = this.size;int i = 0;//此处使用了label语法,用来方便跳出循环//此代码块是为了找到对应的元素found: {if (o == null) {//如果对象为null,就从小到大的遍历,删除null,再跳出循环for (; i < size; i++)if (es[i] == null)break found;} else {//如果元素不为null,就从小到大的遍历集合,使用equals判断元素是否相等,再跳出循环for (; i < size; i++)if (o.equals(es[i]))break found;}//如果都没有找到,就返回falsereturn false;}//根据刚才遍历的结果,获得对应的索引值,然后再次使用删除方法,进行删除元素(即按照元素删除只是在按照索引删除前多一个内容查找)fastRemove(es, i);return true;
}

4.7、修改集合元素

找到对应的索引,直接修改

//传入需要修改的元素下标,以及需要修改后的内容
public E set(int index, E element) {//校验传入下标是否合理Objects.checkIndex(index, size);//获取对应下标的内容,并且赋值给oldValue变量(方法详情,请查看补充方法十二)E oldValue = elementData(index);//将修改后的内容赋值到对应下标的内容上elementData[index] = element;//返回修改之前的值return oldValue;
}

补充方法十二:

//根绝对应的索引,返回对应索引上的内容
E elementData(int index) {return (E) elementData[index];
}

4.8、获取指定的元素

找到索引,直接返回

public E get(int index) {//检验传入的索引是否符合规范Objects.checkIndex(index, size);//返回对应位置上的元素(方法详情,请查看补充方法十二)return elementData(index);
}

4.9、toString方法

ArrayList集合打印时,自带toString方法

测试代码:

package pers.mobian;import java.util.ArrayList;public class arraylist01 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<String>();list.add("a1");list.add("a2");list.add("a3");list.add("a4");System.out.println(list);if (list instanceof ArrayList) {System.out.println("我是集合类型");}System.out.println("===============");String s1 = list.toString();System.out.println(s1);if (s1 instanceof String) {System.out.println("我是字符串类型");}}
}

执行结果:

[a1, a2, a3, a4]
我是集合类型
===============
[a1, a2, a3, a4]
我是字符串类型

我们不难发现,无论是字符串类型还是集合类型,他们的长相都是一样的。toString方法我们能理解,饭是ArrayList为什么呢???

我们再次进入源码

public void println(Object x) {String s = String.valueOf(x);//加了把锁synchronized (this) {print(s);//换行newLine();}
}
public static String valueOf(Object obj) {return (obj == null) ? "null" : obj.toString();
}
public String toString() {//获取对应的迭代器对象Iterator<E> it = iterator();//判断元素是否为null,如果为null直接返回[]if (! it.hasNext())return "[]";//创建一个StringBuilder类的对象StringBuilder sb = new StringBuilder();sb.append('[');//遍历迭代器中的元素,然后不断地追加元素for (;;) {//获取迭代器对象的元素赋值给e(方法详情,请查看补充方法十三)E e = it.next();sb.append(e == this ? "(this Collection)" : e);if (! it.hasNext())//返回时调用了toString方法return sb.append(']').toString();sb.append(',').append(' ');}
}

补充方法十三:该方法是ArrayList获取的迭代器对象的next方法,与LinkList下的next方法有区别(此处也可以看出两种集合结构的区别)

//获取迭代器对象的下一个元素
public E next() {//检查修改的次数,如果修改的次数和预期的修改次数不一样,就会抛出异常checkForComodification();int i = cursor;if (i >= size)throw new NoSuchElementException();//将旧的集合赋值到新创建的Object数组中Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;//返回刚刚经过的元素return (E) elementData[lastRet = i];
}

我们查看源码发现打印输出集合的时候,系统会执行对应的toString方法,即我们看到的集合形式和我们用toString方法打印的相同

4.10、获取迭代器的方法

该方法在分析LinkList使用迭代器遍历的时候已经讲解过,有兴趣的拉到开头去瞅瞅

至此,ArrayList的增删改查方法的执行流程已经讲解完毕,在面试中一般是结合LinkList进行考察。原理部分的总结可以参考我的其他博客
传送门:
ArrayList原理讲解

一句一句的读ArrayList源码(代码基于JDK11)相关推荐

  1. ArrayList源码解析——基于JDK1.8

    1 常量介绍 1.1 ArrayList常量 /*** 默认初始容量* Default initial capacity.*/private static final int DEFAULT_CAPA ...

  2. Java 集合系列(2): ArrayList源码深入解析和使用示例

    戳上面的蓝字关注我们哦! 精彩内容 精选java等全套视频教程 精选java电子图书 大数据视频教程精选 java项目练习精选 概要 上一章,我们学习了Collection的架构.这一章开始,我们对C ...

  3. 增加数组下标_数组以及ArrayList源码解析

    点击上方"码之初"关注,···选择"设为星标" 与精品技术文章不期而遇 前言 前一篇我们对数据结构有了个整体的概念上的了解,没看过的小伙伴们可以看我的上篇文章: ...

  4. java function获取参数_「Java容器」ArrayList源码,大厂面试必问

    ArrayList简介 ArrayList核心源码 ArrayList源码分析 System.arraycopy()和Arrays.copyOf()方法 两者联系与区别 ArrayList核心扩容技术 ...

  5. 【读fastclick源码有感】彻底解决tap“点透”,提升移动端点击响应速度

    前言 近期使用tap事件为老夫带来了这样那样的问题,其中一个问题是解决了点透还需要将原来一个个click变为tap,这样的话我们就抛弃了ie用户 当然可以做兼容,但是没人想动老代码的,于是今天拿出了f ...

  6. 读Zepto源码之Deferred模块

    Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...

  7. 奥莉嘎!!!ArrayList源码中remove、removeAll、clear方法我又肝了一遍,收获良多

    前言 点赞在看,养成习惯. 点赞收藏,人生辉煌. 点击关注[微信搜索公众号:编程背锅侠],第一时间获得最新文章. 看源码血泪史 刚开始工作面试的时候,面试官经常问ArrayList源码相关的问题,基本 ...

  8. ArrayList源码扩容机制分析

    ArrayList源码&扩容机制分析 发上等愿,结中等缘,享下等福 文章目录 ArrayList源码&扩容机制分析 1. ArrayList 简介 1.1. Arraylist 和 V ...

  9. 【Java源码分析】Java8的ArrayList源码分析

    Java8的ArrayList源码分析 源码分析 ArrayList类的定义 字段属性 构造函数 trimToSize()函数 Capacity容量相关的函数,比如扩容 List大小和是否为空 con ...

  10. 读Zepto源码之操作DOM

    2019独角兽企业重金招聘Python工程师标准>>> 这篇依然是跟 dom 相关的方法,侧重点是操作 dom 的方法. 读Zepto源码系列文章已经放到了github上,欢迎sta ...

最新文章

  1. Pip install: ImportError: cannot import name IncompleteRead
  2. asp.net播放声音
  3. c++美发店管理系统设计_美发店如何打造会员管理系统?掌柜智囊—收银机必不可少...
  4. php 慢查询日志设置查看
  5. 听“汉代风云”,看“晁错之死”
  6. 高手进阶:/etc/profile环境变量配置解析
  7. kafka管理神器-kafkamanager
  8. 页面级缓存@ OutputCache
  9. cmake取消宏定义_Excel基础丨取消excel中宏安全提示框
  10. php积极拒绝,linux安装了xunsearch服务,但是PHP连接显示:目标计算机积极拒绝
  11. 2016年度太和顾问北京高科技行业人力资本数据信息发布
  12. 考计算机一级用什么软件学,大学计算机一级考试用的是什么word软件
  13. 贾俊平《统计学基于R》(第三版)第八章方差分析习题答案
  14. isilon 时间设置
  15. 计算机竞赛制作机器人,2016全国中小学电脑制作比赛机器人灭火竞赛规则
  16. GitHub创建仓库
  17. Life with qmail -- 中文版(英文版本2 Jan 2006)
  18. 计算机专业大一期末总结
  19. (附源码)SSM校园新闻发布系统JAVA计算机毕业设计项目
  20. mipi sensor 调试流程

热门文章

  1. python循环的基本思想是重复_python基础-循环
  2. 计算机辅助几何设计等值曲线,中国科学技术大学硕士专业:计算机辅助几何设计...
  3. 计算机导论的计算题,计算机导论复习题(选择部分)汇总
  4. git revert 之后怎么撤销_Git撤销回滚操作(git reset 和 get revert)
  5. 刘明计算机学院,西南大学计算机与信息科学学院研究生导师简介-刘明
  6. mui ajax的值php怎样获取,关于mui.ajax的设置,以及php取不到data值的问题的方法
  7. powerbi python词云图_Power BI 标签云可视化
  8. 树莓派串口通信编码_树莓派和STM32通过USB和串口通信记录
  9. python urllib3离线安装_离线安装spyder的Python环境
  10. 韩顺平 jdbc 之 mysql,(韩顺平讲解)jdbc学习(四)---java连接mysql