文章目录
引用
本文部分图片源码文字来源于
1.java集合详解 - 不眠不休不写bug的文章 - 知乎 https://zhuanlan.zhihu.com/p/137999373
2.java中modCount 参数的用处http://t.csdn.cn/GvPDO
为什么需要集合?
java集合是什么?
java集合实际上是一种经常被运用到的java类库,其中提供了已经实现的的数据结构,省去了程序员再次编写数据结构的事情.在Leetcode中经常会被用到,有很重要的作用.
1.分类
Java中的集合一般分为两类:
一是继承了接口Collection的Set和List接口(单列集合),一是继承了Map接口的双列集合(存放的是键值对)。
2.List接口的底层操作扩容机制和源码(以ArrayList为例)
以一段非常简单的代码为例并进入debug
public class Main {
public static void main(String[] args) {
ArrayList pet = new ArrayList();
pet.add("cat");
pet.add("dogs");
pet.add("cat");
pet.add("goose");
System.out.println(pet);
}
}
调用无参构造器进行一个空的ArrayList对象的创建
(1)底层定义的参数
通过断点进入后,在添加了一个对象时进入其底层源码。ArrayList在底层维护了一个Object类型的数组elementData,并且定义了一个默认大小为10的DEFAULT_CAPACITY。并且将默认的空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData。
之后进入第一个对象添加时的源码:
首先可以从debugger看到关于这个ArrayList对象的一些基本信息:因为该集合为空,所以size=0,需要添加的字符串“cat”被传给了e对象。可以看到ArrayList类中的add方法传入一个类型为E(泛型)的e对象,进入了一个叫ensureCapacityInternal的方法,需要传进该方法的参数为 size+1**,此时为1,我们再次debug进入该方法:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
再次进入方法**ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))中的calculateCapacity(elementData, minCapacity)**方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
在调用了无参构造器后第一个if判断值为真,进入方法Math.max(DEFAULT_CAPACITY, minCapacity),其中的DEFAULT_CAPACITY为类ArrayList的默认大小10,所以返回Math.max(10,1),即返回10.之后进入方法 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))其中传入的参数即为返回值10:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
其中有一个参数modCount,在ArrayList的源码中并没有该参数的定义,所以我查阅了相关资料:
(2)modCount的含义:
大致的意思是:modCount这个参数记录了 某个List改变大小的次数,如果modCount改变的不符合预期,那么就会抛出异常。
好的了解了大概含义,我们继续进行源码分析:
if (minCapacity - elementData.length > 0)
grow(minCapacity);
其中的minCapacity就是传入的10,而elementData.length为数组elementData的长度,此时的elementData在前几步的操作中被赋为默认的空数组,所以其长度自然是0,满足此if的条件,所以进入grow(minCapacity)方法:
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);
}
其中的newCapacity经过操作 int newCapacity = oldCapacity + (oldCapacity >> 1)大小为oldCapacity的1.5倍,经过判断
if (newCapacity - minCapacity < 0)成立,此时将执行操作newCapacity = minCapacity,即该数组的新容量仍然为minCapacity,即此时的数组容量依然是够用的,不需要额外扩容,若(newCapacity - minCapacity < 0)不成立,则newCapacity为oldCapacity的1.5倍进行扩容。
最后将newCapacity的大小赋值给数组elementData的大小。即完成了底层数组的第一次扩容。
后继续进行add方法:
将e对象即传入的字符串赋值给elementData[size++],添加成功则返回true.
以上即为ArrayList的底层扩容机制。