Java中Collection一直都是一个庞大的家族,他们错综复杂,而又功能强大,在什么时候采用什么样的类型也是一个十分讲究的问题,现在我们先来看看Collection家族的族谱
URD的操作委托给了Iteartor接口,这就是大名鼎鼎的迭代器模式;
ArrayList是首先开始着手细节实现的,我们从他开始看:
private transient Object[] elementData;
可见在聚集内部是利用数组实现的,数组和聚集的区别就在于,我们对于数组必须在初始化的时候就开始
指定其容量,而我们对于聚集则可以不必,为什么可以不用呢?
实例化的时候初始化容积;
不指定初始化容积的时候为10,可见如果我们的元素自认为是小于10的话,那么我们可以指定不指定容积
,否则的话,最好指定,不然会带来性能上的损耗;
modCount为修改次数;减去一些不存在的值;
指定一个最小容积,如果当前容量小于最小容积,则试着扩大1.5倍(扩展因子)如果扩展侯还小于最小容积,那么就扩大容积到该最小容积,并且多出来的值会使用默认值填充;
克隆唯一的不同就是把修改次数给重置了;
copyOf是1.6新增的方法,他返回的是一个新的深层次的拷贝;
他的查找方法和赋值方法都是数组的实现,关键的在于其的增加和删除等等方法,如果自动的扩展容量;
首先得保证容量没有溢出:
一般来说当容量不够的时候会扩展1.5倍;
从这里我们可以看出容量和Size的不同
elementData[size++] = e;
上面增加容量的时候,容量已经扩展到了原来的1.5倍,但是我们的size没有变,size变化的条件是增加或者删除一个元素的时候+1或者-1,用户无法指定的,他是该聚集中实实在在存在的元素的个数;刚才是从末尾增加一个元素,下面是从中间增加一个元素,那么这个元素以后的其他元素应该往后挪动一位;
下面是修改的方法:
Vector和ArrayList最大的不同就是:
1)vector是线程安全的,其操作基本上都带这Synchronized的表示哦
2)
我们开始在ArrayList中说了size不是用户能自定义的,那么在Vector中就可以;如果size比当前elementCount大,那么就会扩展这个数组,并且用null来填充;可以理解为一次性加多个null元素;
capacity还是和ArrayList一样,是容量;但是vector暴露出来了
当前的元素个数,其中包括setSize的null值元素;我想在ArrayList中不把setSize和capacity暴露出来,主要也是为了考虑线程安全;
3)其含有一个Enumeration操作,能够返回一个遍历的枚举
枚举和开始的迭代器区别主要在于枚举是线程安全的;
4)可以指定capacityIncrement,当增加元素时不够。则增加这么空间,如果不指定,那么是增加两倍,而不是1.5
当setSize的时候,最先是增加capacityIncrement或者两倍,如果不够的话,那么就增加到新的size,就一位这下一次就算再添加一个元素,也会再加上capacityIncrement或者两倍;'
接下来我们来看LinkedList,以上两种List都是根据数组来实现的,我们知道数组最大的好处就是随机访问,在内存中,我们可以根据数组起始位置和索引能够很快的定位到一个元素,而我们增删元素,尤其是在中间增删的时候,我们不得不把元素挪动,这个工程是十分巨大的;
下面我们来看下LinkedList的实现,首先他定义了节点:
这个节点是一个双向节点,既有头也有尾;
初始化的时候就只有一个空的头;
都指向其本身,形成一个环;
拿到第一个元素,可见初始化的那个头是一个标记,是不含元素的;
用这个空的Entry连接了头和尾,这样我们就可以轻松的从两头开始遍历,而这个header的引用成为了这个环的分界点;
下面是对表头表尾的操作:
插入节点:
有了header的双向连接,十分方便;
相当于在链表末尾添加节点,和addLast一样;
移除节点:
克隆方法:
注意,是浅克隆;
clone.add(e.element):其中,element没有变化;
打印syna;
这一点倒是list都是相同的;ArrayList等也一样;这一下,我们对List是不是有一定的了解,这些主要涉及到数据结构,是很好的学习材料;
URD的操作委托给了Iteartor接口,这就是大名鼎鼎的迭代器模式;
ArrayList是首先开始着手细节实现的,我们从他开始看:
private transient Object[] elementData;
可见在聚集内部是利用数组实现的,数组和聚集的区别就在于,我们对于数组必须在初始化的时候就开始
指定其容量,而我们对于聚集则可以不必,为什么可以不用呢?
- private int size;
- public ArrayList(int initialCapacity) {
- super();
- if (initialCapacity < 0)
- throw new IllegalArgumentException("Illegal Capacity: "+
- initialCapacity);
- this.elementData = new Object[initialCapacity];
- }
实例化的时候初始化容积;
- public ArrayList() {
- this(10);
- }
,否则的话,最好指定,不然会带来性能上的损耗;
- public void trimToSize() {
- modCount++;
- int oldCapacity = elementData.length;
- if (size < oldCapacity) {
- elementData = Arrays.copyOf(elementData, size);
- }
- }
- public void ensureCapacity(int minCapacity) {
- modCount++;
- int oldCapacity = elementData.length;
- if (minCapacity > oldCapacity) {
- Object oldData[] = elementData;
- int newCapacity = (oldCapacity * 3)/2 + 1;
- if (newCapacity < minCapacity)
- newCapacity = minCapacity;
- // minCapacity is usually close to size, so this is a win:
- elementData = Arrays.copyOf(elementData, newCapacity);
- }
- }
- public Object clone() {
- try {
- ArrayList<E> v = (ArrayList<E>) super.clone();
- v.elementData = Arrays.copyOf(elementData, size);
- v.modCount = 0;
- return v;
- } catch (CloneNotSupportedException e) {
- // this shouldn't happen, since we are Cloneable
- throw new InternalError();
- }
- }
- public Object[] toArray() {
- return Arrays.copyOf(elementData, size);
- }
copyOf是1.6新增的方法,他返回的是一个新的深层次的拷贝;
他的查找方法和赋值方法都是数组的实现,关键的在于其的增加和删除等等方法,如果自动的扩展容量;
- public boolean add(E e) {
- ensureCapacity(size + 1); // Increments modCount!!
- elementData[size++] = e;
- return true;
- }
- public void ensureCapacity(int minCapacity) {
- modCount++;
- int oldCapacity = elementData.length;
- if (minCapacity > oldCapacity) {
- Object oldData[] = elementData;
- int newCapacity = (oldCapacity * 3)/2 + 1;
- if (newCapacity < minCapacity)
- newCapacity = minCapacity;
- // minCapacity is usually close to size, so this is a win:
- elementData = Arrays.copyOf(elementData, newCapacity);
- }
- }
从这里我们可以看出容量和Size的不同
elementData[size++] = e;
上面增加容量的时候,容量已经扩展到了原来的1.5倍,但是我们的size没有变,size变化的条件是增加或者删除一个元素的时候+1或者-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!!
- System.arraycopy(elementData, index, elementData, index + 1,
- size - index); //挪动一位;
- elementData[index] = element; //插入一个新的元素;
- size++; //增加当前元素的个数;
- }
下面是修改的方法:
- public E remove(int index) {
- RangeCheck(index); //判断索引位置是否溢出;
- modCount++; //增加修改次数
- E oldValue = (E) elementData[index]; //保存被删除的元素,以待返回而不被垃圾回收
- int numMoved = size - index - 1;
- if (numMoved > 0)
- System.arraycopy(elementData, index+1, elementData, index,
- numMoved); //往前挪动
- elementData[--size] = null; // Let gc do its work //那么最后的元素不能还存在,那么
- 置成空的等待回收
- return oldValue;
- }
Vector和ArrayList最大的不同就是:
1)vector是线程安全的,其操作基本上都带这Synchronized的表示哦
2)
- public synchronized void setSize(int newSize) {
- modCount++;
- if (newSize > elementCount) {
- ensureCapacityHelper(newSize);
- } else {
- for (int i = newSize ; i < elementCount ; i++) {
- elementData[i] = null;
- }
- }
- elementCount = newSize;
- }
- public synchronized int capacity() {
- return elementData.length;
- }
capacity还是和ArrayList一样,是容量;但是vector暴露出来了
- public synchronized int size() {
- return elementCount;
- }
3)其含有一个Enumeration操作,能够返回一个遍历的枚举
- public Enumeration<E> elements() {
- return new Enumeration<E>() {
- int count = 0;
- public boolean hasMoreElements() {
- return count < elementCount;
- }
- public E nextElement() {
- synchronized (Vector.this) {
- if (count < elementCount) {
- return (E)elementData[count++];
- }
- }
- throw new NoSuchElementException("Vector Enumeration");
- }
- };
- }
枚举和开始的迭代器区别主要在于枚举是线程安全的;
4)可以指定capacityIncrement,当增加元素时不够。则增加这么空间,如果不指定,那么是增加两倍,而不是1.5
- private void ensureCapacityHelper(int minCapacity) {
- int oldCapacity = elementData.length;
- if (minCapacity > oldCapacity) {
- Object[] oldData = elementData;
- int newCapacity = (capacityIncrement > 0) ?
- (oldCapacity + capacityIncrement) : (oldCapacity * 2);
- if (newCapacity < minCapacity) {
- newCapacity = minCapacity;
- }
- elementData = Arrays.copyOf(elementData, newCapacity);
- }
- }
接下来我们来看LinkedList,以上两种List都是根据数组来实现的,我们知道数组最大的好处就是随机访问,在内存中,我们可以根据数组起始位置和索引能够很快的定位到一个元素,而我们增删元素,尤其是在中间增删的时候,我们不得不把元素挪动,这个工程是十分巨大的;
下面我们来看下LinkedList的实现,首先他定义了节点:
- private static class Entry<E> {
- E element;
- Entry<E> next;
- Entry<E> previous;
- Entry(E element, Entry<E> next, Entry<E> previous) {
- this.element = element;
- this.next = next;
- this.previous = previous;
- }
- }
这个节点是一个双向节点,既有头也有尾;
- private transient Entry<E> header = new Entry<E>(null, null, null);
- private transient int size = 0;
- public LinkedList() {
- header.next = header.previous = header;
- }
- public E getFirst() {
- if (size==0)
- throw new NoSuchElementException();
- return header.next.element;
- }
拿到第一个元素,可见初始化的那个头是一个标记,是不含元素的;
- public E getLast() {
- if (size==0)
- throw new NoSuchElementException();
- return header.previous.element;
- }
下面是对表头表尾的操作:
插入节点:
- private Entry<E> addBefore(E e, Entry<E> entry) {
- Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
- newEntry.previous.next = newEntry;
- newEntry.next.previous = newEntry;
- size++;
- modCount++;
- return newEntry;
- }
- public E removeFirst() {
- return remove(header.next);
- }
- public E removeLast() {
- return remove(header.previous);
- }
- aram e the element to add
- */
- public void addFirst(E e) {
- addBefore(e, header.next);
- }
- public void addLast(E e) {
- addBefore(e, header);
- }
有了header的双向连接,十分方便;
- public boolean add(E e) {
- addBefore(e, header);
- return true;
- }
移除节点:
- private E remove(Entry<E> e) {
- if (e == header)
- throw new NoSuchElementException(); //头肯定是不能移除的
- E result = e.element;
- e.previous.next = e.next;
- e.next.previous = e.previous;
- e.next = e.previous = null; //方便垃圾回收啦;
- e.element = null; //这样的移除我们可以不用挪动整个队列了
- size--;
- modCount++;
- return result;
- }
- public Object clone() {
- LinkedList<E> clone = null;
- try {
- clone = (LinkedList<E>) super.clone();
- } catch (CloneNotSupportedException e) {
- throw new InternalError();
- }
- // Put clone into "virgin" state
- clone.header = new Entry<E>(null, null, null);
- clone.header.next = clone.header.previous = clone.header;
- clone.size = 0;
- clone.modCount = 0;
- // Initialize clone with our elements
- for (Entry<E> e = header.next; e != header; e = e.next)
- clone.add(e.element);
- return clone;
- }
注意,是浅克隆;
clone.add(e.element):其中,element没有变化;
- package org.corey.demo;
- public class Person {
- private String name;
- public Person(String name) {
- super();
- this.name = name;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- public class Test {
- /**
- * @param args
- */
- public static void main(String[] args) {
- Person p1=new Person("corey");
- LinkedList l=new LinkedList();
- l.add(p1);
- LinkedList l2=(LinkedList)l.clone();
- ((Person)l.get(0)).setName("syna");
- System.out.println(((Person)l2.get(0)).getName());
- }
- }
打印syna;
这一点倒是list都是相同的;ArrayList等也一样;这一下,我们对List是不是有一定的了解,这些主要涉及到数据结构,是很好的学习材料;