Array
array:数组,容量固定,无法改变。
创建数组(一定要声明长度):int[] i=new int[4];或者int[] i={1,2,3};//{}里元素的个数就是数组的长度
二维数组可以这样声明int[][] arr=new int[3][];但是不允许int[][] arr=new int[][3];对二维复合数据类型的数组,必须首先为最高维分配引用空间,然后再顺次为低维分配空间。而且,必须为每个数组元素单独分配空间。
byte/short/int类型数组元素默认值为0,long类型数组元素默认值为0l,float类型数组元素默认值为0.0f,double类型数组元素默认值为0.0d,boolean类型数组元素默认值为false。
使用数组内置的length属性获得数组的大小。试用Arrays的静态函数对数组进行操作。例如Arrays.fill(array,1)将array数组元素全部填充为1。使用Arrays.asList(array)将数组array转为List。
利用下面这个函数实现数组复制。src:源数组,index:源数组开始复制的元素下标,dest:目标数组,destIndex:复制元素存放在目标数组起始位置,length:复制的长度
System.arraycopy(src, int index, dest, int destIndex, int length):
数组的下标从0开始,获得数字某一位置的元素可以通过其下标获得。为什么可以通过数组的下标获得对应的对象:
数组在内存是连续存储的,数组名是这段内存的起始地址,而数组的下标,可以理解成一个偏移量。而对于数组array[i]就相对于array便宜了i个单位。通过array+i就可以得到array[i]的内存地址,则可以读出该地址上的对象。
Vector
vector:矢量队列,容量可以自动增长。实现了List, RandomAccess, Cloneable, java.io.Serializable接口,因此它有List的增删改查等功能。RandomAccess接口的随机访问功能,即我们可以通过元素的序号快速的获取元素。Cloneable实现了clone函数,使得vector能被克隆。vector的add,remove等操作都用了 synchronized修饰,因此vector是线程安全的。
vector部分代码:
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//该数组保存Vector中的数据
protected Object[] elementData;
//数据的数量
protected int elementCount;
//容量的增长系数
protected int capacityIncrement;
private static final long serialVersionUID = -2767605614048989439L;
//指定初始容量大小和增长系数的构造函数
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//指定容量大小的构造函数
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//默认构造函数,初始容量为10
public Vector() {
this(10);
}
//指定集合内容的构造函数
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
}
vector自动增长容量是怎么实现的呢?
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);//当前实际容量+1是否超过最大容量
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)//如果实际容量+1大于数组的长度
grow(minCapacity);//增加容量
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//如果当前数组的长度不足,新建数组,如果容量增长系数大于0,则新数组长度为旧数组长度+容量增长系数,否则新数组的长度=旧数组长度*2;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
vector在指定位置新增元素
public void add(int index, E element) {
insertElementAt(element, index);
}
public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);//扩容
//扩容后把插入点后面的元素同一往后移一位
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
vector删除元素,会将删除点后的元素往前移动一位。
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);//找到删除的元素并删除后,将删除点后面的元素往前移动一位
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);//找到删除的元素并删除后,将删除点后面的元素往前移动一位
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
LinkedList
LinkedList:基于双向链表的动态数组,非线程安全。实现了List和Deque。所以LinkedList实现了List的操作,数据的增删改查。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;//容量
//首节点
transient Node<E> first;
//尾节点
transient Node<E> last;
//默认构造函数
public LinkedList() {
}
//指定集合初始元素的构造函数
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
}
LinkedList在指定位置新增元素源码:
public void add(int index, E element) {
checkPositionIndex(index);//检查位置是否超出范围
if (index == size)//插入位置在链表尾
linkLast(element);
else
linkBefore(element, node(index));//在指定节点前插入新元素
}
private void checkPositionIndex(int index) {//检查插入位置是否超出范围,超出了就抛出异常
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;//插入位置不能小于0也不能超过链表的长度
}
void linkLast(E e) {
final Node<E> l = last;//获得当前尾节点
final Node<E> newNode = new Node<>(l, e, null);//创建新节点
last = newNode;//将新节点设置为尾节点
if (l == null)
first = newNode;//如果之前尾节点为空,首节点即尾节点
else
l.next = newNode;//如果之前尾节点不为空,之前尾节点的后一个就是当前尾节点
size++;
modCount++;
}
void linkBefore(E e, Node<E> succ) {//在指定节点前插入节点
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
参考资料: Java之LinkedList源码解读(JDK 1.8)
ArrayList
ArrayList:动态数组,容量可以动态增长。非线程安全。
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;
//指定初始容量的构造函数
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) {
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在指定位置中插入某一对象,源码:
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++;
}
private void rangeCheckForAdd(int index) {// 要插入的位置是否合法(溢出)
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 检查数组的大小是否需要扩容
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);/使用默认值和参数中较大的值作为容量的预设值
}
ensureExplicitCapacity(minCapacity);//检查是否需要扩容
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {// 扩容
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);// 新长度=旧长度+旧长度/2
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);// 将原来的数组内容复制到新数组中
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
ArrayList的删除指定位置的元素
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 rangeCheck(int index) {//检查删除的位置是否溢出
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
ArrayList,Vector,LinkedList,Array之间的区别
ArrayList:数组队列,容量可以动态增长(效率较低),扩容需要把旧数组里的内容复制到新数组中。
Vector:线程安全,性能差于ArrayList。
LinkedList:基于链表,允许null元素,没有同步方法,非线程安全。不支持随机访问。
array:数组,容量固定,无法改变。
ArrayList与Vector区别:Vector是线程安全的,ArrayList是非线程安全的。从源码中我们能看到Vector扩容是新容量=原容量*2,而ArrayList的则是新容量=原容量*1.5。Vector可以设置增长系数,而ArrayList不行。
ArrayList与LinkedList区别ArrayList适用于随机访问较多的情况下,但是对于随机插入元素则效率较低。如果ArrayList在非数组尾部插入元素,首先要判断插入的位置是否溢出,然后判断是否需要扩容(如果需要扩容,还需要把旧数组复制到新数组中)。然后再把插入点的元素后移一位。最后才插入元素。而LinkedList只需要记录插入点的前后项即可。而对于获得某位置上的数据,ArrayList可以通过下标获得,而LinkedList则需要进行向前或者向后的遍历才行。
在使用ArrayList等集合类时,如果提前知道长度,应该在初始化时设置长度,防止新增元素后的扩容,提高代码效率或者使用array代替ArrayList。如果在代码中列表常见操作是随机插入或删除,则使用LinkedList代替ArrayList,如果常见操作是随机访问,则使用ArrayList。