内容简介:由于1.7和1.8几乎没什么变化,本文以jdk1.8为例。编写测试用例,打上断点:
1,ArrayList面试必问
-
说说ArrayList和LinkedList的区别?
ArrayList基于数组实现,LinkedList基于链表实现,不同的数据结构决定了ArrayList查询效率比较高,而LinkedList插入删除效率比较高,反过来就比较慢了。
-
ArrayList默认初始容量为多少?按照几倍来扩容?
10,1.5倍。
-
说说数组扩容的原理?
ArrayList扩容调用的是Array.copyof函数,把老数组遍历赋值给新数组返回。
-
说说ArrayList常见方法的时间复杂度?
- get方法通过下标获取元素,时间复杂度为O(1)
- add方法直接添加会添加到集合的尾部,时间复杂度为O(1)
- add方法通过下标添加到非尾部会引起数组的批量移动,时间复杂度为O(n),否则为O(1)
- remove方法通过下标删除非尾部元素引起数组批量移动,时间复杂度为O(n),反之则为O(1)
- remove方法根据对象删除需要遍历集合,时间复杂度为O(n),如果删除的为非尾部元素,会使数组批量移动,时间复杂度为O(n^2)
总之,通过下标操作的时间复杂度为O(1),如果触发了数组的批量移动,时间复杂度为O(n),如果通过对象操作需要遍历集合,时间复杂度已经为O(n),若同时触发了数组的移动,时间复杂度为O(n^2).
-
ArrayList和vector的区别
- 最大的区别在于线程是否安全
- 其次Vector是两倍扩容
- 最后就是在不指定大小的情况下,ArrayList容量初始化是在添加元素的时候,而Vector有一个无参构造器直接初始化为10
2,Debug ArrayList源码
由于1.7和1.8几乎没什么变化,本文以jdk1.8为例。
2.1 用Debug分析一个元素是如何add进ArrayList
编写测试用例,打上断点:
先分析构造函数如何初始化,关键步骤如下:
elementData是ArraList底层数组的实现,(ps:hashMap数组使用table命名)
DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示默认的空数组,也就是说ArrayList在构造函数初始化时并不会进行底层数组的初始化。
给元素的添加打上断点,分析过程:
进入add方法内部:
public boolean add(E e) { //确保内部容量,在元素添加进来前可能要进行扩容操作,size初始化为0,表示集合的长度 ensureCapacityInternal(size + 1); // Increments modCount!! //添加元素,size自增 elementData[size++] = e; return true; }
进入ensureCapacityInternal方法内部:此时elementData为空,size+1=minCapacity=1
ensureExplicitCapacity:确保明确的能力
计算容量,calculateCapacity方法:
private static int calculateCapacity(Object[] elementData, int minCapacity) { //判断数组是否为空,若为空,返回默认容量和最小容量的最大值,若不为空,返回最小容量 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
DEFAULT_CAPACITY默认容量为10:
继续分析,进入:
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));此时参数为10,也就是ArrayList的默认容量
private void ensureExplicitCapacity(int minCapacity) { modCount++; //集合的修改次数 //如果最小容量减去数组长度大于0,进行扩容,此时最小容量为10,数组长度为0 if (minCapacity - elementData.length > 0) grow(minCapacity); }
核心扩容函数grow:(ps:HashMap中扩容函数为resize)
private void grow(int minCapacity) { //oldCapacity:旧数组容量 int oldCapacity = elementData.length; //新容量等于旧容量加上旧容量的一半,>>1相当于除以2(ArrayList扩容是1.5倍) int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量小于最小容量,则赋值为最小容量,此时newCapacity等于0,minCapacity为10 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //赋值给新数组 elementData = Arrays.copyOf(elementData, newCapacity); }
数组复制Arrays.copyOf:
2.2 用Debug分析如何通过数组下标获取ArrayList元素
打上断点,debug:
首先进行范围检查,而后返回元素
2.3 用Debug分析如何通过数组下标删除一个元素
打上断点:
进入remove方法内部,
public E remove(int index) { //下标范围检查 rangeCheck(index); //修改次数自增 modCount++; //保留当前删除元素的值,稍后返回 E oldValue = elementData(index); //需要移动元素的个数 int numMoved = size - index - 1; if (numMoved > 0) //底层使用native方法,debug进不去。native方法:java调用其他语言的接口 System.arraycopy(elementData, index+1, elementData, index, numMoved); //最后一位置空 elementData[--size] = null; // clear to let GC do its work //返回删除元素的值 return oldValue; }
2.4 用Debug分析如何通过对象删除一个元素
进入remove方法:
public boolean remove(Object o) { //如果对象为空,则遍历ArrayList集合 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; }
进入fastRemove方法:
private void fastRemove(int index) { modCount++; //numMoved:需要移动数组的个数 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 wrk }
2.5 用Debug分析向数组中间添加元素
进入add方法
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++; }
关于System.arraycopy时间复杂度问题,在添加或者删除最后一个元素的时候不会触发数组的复制机制,时间复杂度为O(1),若是添加到数组中间,由于会触发数组的复制,时间复杂度为O(n)。对于删除元素同样,根据数组下标删除的情况下,删除尾部元素是不会触发数组的扩容机制的,若删除中间的元素,同样会触发数组的复制。若根据对象删除元素,由于本身遍历到对象的时间复杂度为O(n),删除元素后再对数组进行重组,所以时间复杂度为O(n^2)。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python编程
[美]埃里克·马瑟斯 / 袁国忠 / 人民邮电出版社 / 2016-7-1 / CNY 89.00
本书是一本针对所有层次的Python 读者而作的Python 入门书。全书分两部分:第一部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy 和Pygal 等强大的Python 库和工具介绍,以及列表、字典、if 语句、类、文件与异常、代码测试等内容;第二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D 游戏开发如何利用数据生成交互式的信息图......一起来看看 《Python编程》 这本书的介绍吧!