ArrayList底层内存结构
ArrayList的扩容机制
深、浅拷贝
标记接口:解析到标记接口,做一些处理
RandomAccess:表明该类支持随机访问(下标访问,就是告诉别人你底层用的是数组)
Cloneable:表明该类支持克隆(深拷贝,浅拷贝)
/*
JDK1.7字符串常量池是在堆(常量池)中的
JDK1.8做了优化,出现一个新概念:元空间(又叫非堆),元空间并不在虚拟机中,而是使用本地内存(可调节)
*/
String aa=new String("aa");
String bb="a"+"a";
System.out.println(aa==bb);//false 指针相同,但是不相等
System.out.println(aa.hashCode());
System.out.println(bb.hashCode());
System.out.println(aa.equals(bb));//true
String t1="123"+"4";
String t2="1234";
System.out.println(t1==t2);//true String拼接是相同的
System.out.println(("123"+"4").hashCode());
System.out.println("1234".hashCode());
System.out.println(t1.equals(t2));
String r1=new String("123"+"4");
String r2=new String("1234");
System.out.println(r1==r2);//false
System.out.println(r1.hashCode());
System.out.println(r2.hashCode());
System.out.println(r1.equals(r2));//true
Serializable:序列化
public class Demo02 implements Serializable, Cloneable {
private int id=11;
private String name="1231321";
private Demo01 demo01=new Demo01();
//省略set/get
@Override
protected Object clone() throws CloneNotSupportedException {
//第一种方式(比较死板)
Object obj = null;
obj = super.clone();
Demo02 demo02 = (Demo02) obj;
//对引用类型的单独处理
demo02.demo01 = (Demo01) demo01.clone();
return obj;
}
/**
* 使用IO的方式克隆
*
* @return
*/
public Object deepProtoCloneByIO() {
//创建流对象
ByteArrayInputStream bis = null;
ByteArrayOutputStream bos = null;
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);//当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
Demo02 o = (Demo02) ois.readObject();
return o;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
//默认的容量是10
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {
};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
//elementData存储ArrayList内的元素,
/*
transient关键字解释:Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成 员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字 transient
*/
transient Object[] elementData; // non-private to simplify nested class access
//size表示它包含的元素的数量。
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;
}
}
// 用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素。
public E set(int index, E element) {
//检验下他的长度合不合理
RangeCheck(index);
E oldValue = (E) elementData[index];
elementData[index] = element;
return oldValue;
}
// 将指定的元素添加到此列表的尾部。
public boolean add(E e) {
//扩容
/*
先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增
*/
ensureCapacity(size + 1);
elementData[size++] = e;
return true;
}
// 将指定的元素插入此列表中的指定位置。
// 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
// 如果数组长度不足,将进行扩容。
ensureCapacity(size+1); // Increments modCount!!
// 将 elementData中从Index位置开始、长度为size-index的元素,
// 拷贝到从下标为index+1位置开始的新的elementData数组中。
// 即将当前位于该位置的元素以及所有后续元素右移一个位置。
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
// 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此列表的尾部。
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
//扩容
ensureCapacity(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
// 从指定的位置开始,将指定collection中的所有元素插入到此列表中。
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacity(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;
}
/*
首先是检查范围,修改modCount,保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回 被移除的元素。
*/
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;
}
/*
remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。为什么找到了元素就知道了index,不通过remove(index)来移除元素呢?因为fastRemove跳过了判断边界的处理,因为找到元素就相当于确定了index不会超过边界,而且fastRemove并不返回被移除的元素
*/
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;
}
//==============================》fastRemove(int index)
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
}
当ArrayList如果不指定构造个数的话,第一次往里面添加元素时底层数组会初始化一个长度为10的数组,看一下ArrayList里的源码,当添加第11个元素时
再看grow()方法
再看Arrays.copyOf()方法
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/2),但性能会好一些。
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);
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]//这是以新的长度创建一个新的数组
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
//把源数组里的元素拷贝到新数组并返回
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}