目录
1.Arraylist
Arraylist作为常用的数据容器,还是有必要知道一些内部的细节。从线程安全方面来看,Arraylist是非线程安全,假设10个线程同时运行,往Arraylist添加100条数据,有可能出现Arraylist最终的数据总和会小于1000,所以开发中要注意,可以通过锁去解决。而Vector就是线程安全的,不过Vector也有自身的缺点,如果存储大量的数据,Vector会消耗的资源较大,或者做插入、删除、resize等操作都有可能导致Vector的迭代器失效,总之各有优点。
Arraylist内部,就先从构造方法看起:
//内部存储数据,数量变化到一定程度去操作的数组
transient Object[] elementData;
//数据个数
private int size;
//默认初始化容量为10
private static final int DEFAULT_CAPACITY = 10;
//默认空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//指定大小的初始化
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//使用默认空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//容量大小 < 0时候抛出的异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
//无参构造函数,初始化一个空的数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
当我们在初始化Arraylist时,如未指定大小,存储数据的数组会设置默认大小为10。后面当存储数据个数大于10时,Arraylist的存储数组会指向新数组,且新数组的大小为旧数组大小的1.5倍。后面大小如接着超过,会重复这个操作,想想数据量大的时候,使用Arraylist确实会影响性能。下面看看扩容的方法:
private void grow(int minCapacity) {
// 旧数组数量和扩容后的数量
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
//新容量比期望的最小容量小,将期望的最小容量作为当前容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//新容量大于规定最大值,调用大的“扩容”方法hugeCapacity
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//旧的数量容量变大,原数据存入,索引不变
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
//最小期望大小如大于MAX_ARRAY_SIZE,设置新容量为(2^31-1)
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
因为内部是数组,所以保留了索引的概念,因为add和remove和get等方法能很好理解,主要就是add函数多次调用到一定数量会触发"扩容"。
2.LinkedList
LinkedList是一种链表的结构,看看其内部的一个私有类Node,正式这些内部的node节点类组成了链表的形式
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
/**
* Pointer to first node.
* 指向第一个node
*/
transient Node<E> first;
/**
* Pointer to last node.
* 指向最后一个lode
*/
transient Node<E> last;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
......
}
单看一个node类节点。item变量就是本节点值,prev就是前一个节点值,next即为下一个节点值。这样就形成了一条节点关联起来的链式结构。
LinkedList有一些特殊的函数,比如:
public void addFirst(E e) {
linkFirst(e);
}
public void addLast(E e) {
linkLast(e);
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
看名字估计也能知道,就是直接在链表头、尾添加或者删除数据。
3.Vector
Vector为矢量队列,内部存储数据也是数组,采用的存储方式类似于LinkedList的"扩容",先看看它的4个构造方法:
//capacity是Vector的默认容量大小,capacityIncrement是每次Vector容量增加时的增量值。
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//指定Vector的容量大小
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);
}
其空参构造方法,默认创建容量为10的数组。同时也可以指定大小,主要看看capacityIncrement这个参数,解释为"扩容增量",可以先看看下面的代码:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容增量的设置与否影响扩容后的大小
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);
}
可以看到如无设置capacityIncrement,容器数量个数会增长为当前容器数量的一倍。
内部常见函数有:
转载这个老哥的方法整理,有点多:https://www.cnblogs.com/zedosu/p/6662231.html
synchronized boolean add(E object)
void add(int location, E object)
synchronized boolean addAll(Collection<? extends E> collection)
synchronized boolean addAll(int location, Collection<? extends E> collection)
synchronized void addElement(E object)
synchronized int capacity()
void clear()
synchronized Object clone()
boolean contains(Object object)
synchronized boolean containsAll(Collection<?> collection)
synchronized void copyInto(Object[] elements)
synchronized E elementAt(int location)
Enumeration<E> elements()
synchronized void ensureCapacity(int minimumCapacity)
synchronized boolean equals(Object object)
synchronized E firstElement()
E get(int location)
synchronized int hashCode()
synchronized int indexOf(Object object, int location)
int indexOf(Object object)
synchronized void insertElementAt(E object, int location)
synchronized boolean isEmpty()
synchronized E lastElement()
synchronized int lastIndexOf(Object object, int location)
synchronized int lastIndexOf(Object object)
synchronized E remove(int location)
boolean remove(Object object)
synchronized boolean removeAll(Collection<?> collection)
synchronized void removeAllElements()
synchronized boolean removeElement(Object object)
synchronized void removeElementAt(int location)
synchronized boolean retainAll(Collection<?> collection)
synchronized E set(int location, E object)
synchronized void setElementAt(E object, int location)
synchronized void setSize(int length)
synchronized int size()
synchronized List<E> subList(int start, int end)
synchronized <T> T[] toArray(T[] contents)
synchronized Object[] toArray()
synchronized String toString()
synchronized void trimToSize()
4.Stack
称之为栈,本身继承自Vector,有着数据先进后出的特性。
内部存储也是由数组实现
protected Object[] elementData;
常用的几个方法:
//push:将元素添加到数组的末尾
public E push(E item) {
addElement(item);
return item;
}
//加了锁,可以看出push添加数据也线程安全的
public synchronized void addElement(E obj) {
//数量+1,下个索引数据指向参数传递的数据
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
//peek:取出栈顶元素,不执行删除
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
//返回指定索引数据
return elementAt(len - 1);
}
//pop时:取出栈顶元素,并将该元素从栈中删除
public synchronized E pop() {
E obj;
int len = size();
//拿到最后一个数据
obj = peek();
//删除最后一个数据
removeElementAt(len - 1);
return obj;
}
5.hashmap
hashmap内部是16位的数组,数组中存储的每一个元素又是链表的表头,表头这个节点是个Entry类型。
final K key;
V value;
final int hash;
HashMapEntry<K, V> next;
上图看出Entry中包含键对值中的key和value,next指向的是下一个节点Entry。hash作用主要用来计算值存储的位置,公式为:
hash(key)%len。将hash(key)值对len,也就是16取余。