java集合Collection
一些实用的基本的东西,不会去分析UML图。
下面列出一些主要包含关系,淡然集合还有queue队列但不在此处研究
List:可以有重复元素的集合、有序(存入和取出的顺序不一定相同)
- ArrayList:非线程安全、底层是数组的实现,所以方便查询,增删不方便
- LinkedList:非线程安全、底层是链表实现,所以增删方便,查询不方便
- Vector:几乎和ArrayList一样只不过是用来synchronized加锁,所以效率低一点,但是线程安全
Set:不可以有重复元素的集合、无序
- HashSet:线程不安全,存取速度快。底层是以哈希表实现的。
- TreeSet:线程不安全,查询速度快。底层是以红黑树实现的。
- LinkedHashSet:有序,线程不安全,内部是以LinkHashMap实现的。
ArrayList
基于jdk 1.8
常量
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
private static final long serialVersionUID = 8683452581122892189L;
//默认的大小
private static final int DEFAULT_CAPACITY = 10;
//空对象
private static final Object[] EMPTY_ELEMENTDATA = {};
//空对象使用默认构造器时使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//数据存放数组,不参与序列化
transient Object[] elementData; // non-private to simplify nested class access
//长度
private int size;
//最大的大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数
//带初始大小的构造函数
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) {
//将collection对象转换成数组,然后将数组的地址的赋给elementData。
elementData = c.toArray();
//更新size的值,同时判断size的大小,如果是size等于0,直接将空对象赋给elementData
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;
}
}
add
public boolean add(E e) {
//这个方法是确保有位子可以放数据
ensureCapacityInternal(size + 1); // Increments modCount!!
//存放数据,并让size自增
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//如果默认的空对象,则对比默认容量和给定容量,取大
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判断是否需要扩容
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//这个是记录操作次数的,不用管
modCount++;
//判断给定长度是否大于数据长度,如果给定容量大说明放不下,需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//扩容大小为本身大小+本身大小/2,也就是1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//判断是否满足现在的需求,否就直接扩容到现在需要的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//判断是否超出界限
if (newCapacity - MAX_ARRAY_SIZE > 0)
//这个方法里面判断了是否大于int最大上限,如果是就取上限否则就是MAX_ARRAY_SIZE
newCapacity = hugeCapacity(minCapacity);
//复制扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
public void add(int index, E element) {
//这个方法只是判断是否越界,越界就抛出异常
rangeCheckForAdd(index);
//和add方法一样
ensureCapacityInternal(size + 1); // Increments modCount!!
//直接复制index后面的数据给elementData,并在index存入数据
//相当于index后面的数据整体后移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
get
这个太简单了只是验证超界然后从数组里取数据
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
set
简单的判断越界,取原数据存新数据并返回原数据
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
remove
remove有两种一个根据位置 一种根据数据
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;
}
根据对象删除分成两种情况一种是循环判断删除用于删除null,另外一个就是循环equals判断删除,真实的删除方法是fastRemove(int index);
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;
}
真实的删除方法和根据下标删除差不多
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
}
clear
循环清空数据,size变为0,注意容量不变
public void clear() {
modCount++;
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
addAll
两个方法和add方法差不多只不过从添加一个数据变成了,添加多个
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
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;
}
contains
本质就是循环遍历数组看看元素是否存在
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
trimToSize
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
//这个方法主要就是去除size后面的容量
: Arrays.copyOf(elementData, size);
}
}
到这里ArrayList算是差不多了解析了,还有一些内部类,自己的序列化方式等没解析,也不在这里讲了,有兴趣的小伙伴可以自己去看看。
知道上面一些基本就够用了
小结
- ArrayList基于数组方式实现,扩容上限int的最大值
- 添加元素时可能要扩容,删除元素时不会减少容量(若希望减少容量,trimToSize()),删除元素时,将删除掉的位置元素置为null,下次gc就会回收这些元素所占的内存空间。
- 线程不安全
- add(int index, E element):添加元素到数组中指定位置的时候,需要将该位置及其后边所有的元素都整块向后复制一位,所以效率比add低
- remove(Object o)需要遍历数组,效率比不需要遍历数组的remove(int index)低
- 每次扩容默认是1.5倍,如果还不够就是给定的长度
- 默认大小是10