记账凭证做网站摘要怎么写,网页游戏平台代理加盟,wordpress仿站pdf,关键词优化搜索引擎共同点#xff1a;都是线性集合
ArrayList
ArrayList 底层是基于数组实现的#xff0c;并且实现了动态扩容#xff08;当需要添加新元素时#xff0c;如果 elementData 数组已满#xff0c;则会自动扩容#xff0c;新的容量将是原来的 1.5 倍#xff09;#xff0c;来…共同点都是线性集合
ArrayList
ArrayList 底层是基于数组实现的并且实现了动态扩容当需要添加新元素时如果 elementData 数组已满则会自动扩容新的容量将是原来的 1.5 倍来看一下 ArrayList 的部分源码PS以下代码均来自Java8不同版本可能存在细微差距。
public class ArrayListE extends AbstractListEimplements ListE, 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; // 存储元素的数组,数组类型:Objectprivate int size; // 列表的大小即列表中元素的个数
ArrayList 还实现了 RandomAccess 接口这是一个标记接口
public interface RandomAccess {
}
内部是空的标记“实现了这个接口的类支持快速通常是固定时间随机访问”。快速随机访问是什么意思呢就是说不需要遍历就可以通过下标索引直接访问到内存地址。而 LinkedList 没有实现该接口表示它不支持高效的随机访问需要通过遍历来访问元素。
ArrayList 还实现了 Cloneable 接口并且重写了 Object 类的 clone() 方法但只是浅拷贝还是要根据需求使用。 public Object clone() {try {ArrayList? v (ArrayList?) super.clone();v.elementData Arrays.copyOf(elementData, size);v.modCount 0;return v;} catch (CloneNotSupportedException e) {// this shouldnt happen, since we are Cloneablethrow new InternalError(e);}
} ArrayList 还实现了 Serializable 接口支持序列化
但是关键字段 elementData 使用了 transient 关键字修饰这个关键字的作用是让它修饰的字段不被序列化。
看到这里是不是心里出现了很多问好
我们这样来看elementData 是一个数组数组是定长的如果一个新创建的ArrayList并且我们只往里添加了2个元素如果我们默认序列化就会多序列化8个空的内存空间我们再反序列化出来的时候需要更大的空间去接收这个数组。如下例子中可以更好的反应该问题可能出现很大的bug
public class Main {public static void main(String[] args) throws Exception {ListInteger list new ArrayList();for (int i 0; i 100000; i) {list.add(i);}System.out.println(list.size());list.clear();Class? extends List listClass list.getClass();Field field listClass.getDeclaredField(elementData);field.setAccessible(true);Object[] o (Object[]) field.get(list);System.out.println(o.length);}
}输出如下:
100000
106710 于是ArrayList 做了一个愉快而又聪明的决定内部提供了两个私有方法 writeObject 和 readObject 来完成序列化和反序列化。
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException{// Write out element count, and any hidden stuffint expectedModCount modCount;s.defaultWriteObject();// Write out size as capacity for behavioural compatibility with clone()s.writeInt(size);// Write out all elements in the proper order.for (int i0; isize; i) {s.writeObject(elementData[i]);}if (modCount ! expectedModCount) {throw new ConcurrentModificationException();}
}private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {elementData EMPTY_ELEMENTDATA;// Read in size, and any hidden stuffs.defaultReadObject();// Read in capacitys.readInt(); // ignoredif (size 0) {// be like clone(), allocate array based upon size not capacityint capacity calculateCapacity(elementData, size);SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);ensureCapacityInternal(size);Object[] a elementData;// Read in all elements in the proper order.for (int i0; isize; i) {a[i] s.readObject();}}
}
从源码中可以看出序列化和反序列化时只保存了list的大小和所有元素。
还需要注意 ArrayList 在序列化时不允许有并发的修改操作。
Vector/Stack
Vector 也是基于数组实现的但是是线程安全的其他和 ArrayList 基本没有区别源码注释中有句话也可以看出
{code Vector} is synchronized. If a thread-safe
implementation is not needed, it is recommended to use {link
ArrayList} in place of {code Vector}.// Vector的序列化和反序列化的方法与ArrayList略有差异
private void readObject(ObjectInputStream in)throws IOException, ClassNotFoundException {ObjectInputStream.GetField gfields in.readFields();int count gfields.get(elementCount, 0);Object[] data (Object[])gfields.get(elementData, null);if (count 0 || data null || count data.length) {throw new StreamCorruptedException(Inconsistent vector internals);}elementCount count;elementData data.clone();
}private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {final java.io.ObjectOutputStream.PutField fields s.putFields();final Object[] data;synchronized (this) {fields.put(capacityIncrement, capacityIncrement);fields.put(elementCount, elementCount);data elementData.clone();}fields.put(elementData, data);s.writeFields();
} Stack 继承了 Vector同时Stack添加了 push/pop/peek 等方法实现了后进先出LIFO。
LinkedList
LinkedList 是一个继承自 AbstractSequentialList 的双向链表同时实现了 Deque 双向队列接口因此它也可以被当作堆栈、队列或双向队列进行操作。 部分源码
public class LinkedListEextends AbstractSequentialListEimplements ListE, DequeE, Cloneable, java.io.Serializable
{transient int size 0; // 表示链表中的节点个数transient LinkedList.NodeE first; // 链表中的第一个节点transient LinkedList.NodeE last; // 链表中的最后一个节点
可以看到 LinkedList 同样实现了 Serializable 接口支持序列化。但是上面源码中 LinkedList 的所有属性都是 transient 修饰的这又让我们想到了 ArrayList 的序列化实现果然找到了writeObject和readObject方法的实现
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {// Write out any hidden serialization magics.defaultWriteObject();// Write out sizes.writeInt(size);// Write out all elements in the proper order.for (LinkedList.NodeE x first; x ! null; x x.next)s.writeObject(x.item);
}SuppressWarnings(unchecked)
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in any hidden serialization magics.defaultReadObject();// Read in sizeint size s.readInt();// Read in all elements in the proper order.for (int i 0; i size; i)linkLast((E)s.readObject());
}
仔细琢磨发现不仅尽可能少的占用存储空间反序列化时还巧妙的恢复了原来的顺序。