JAVA API备忘----集合

类继承关系图



List:List有两个实现类,ArrayList和LinkedList,前者用数组实现,后者用链表实现,关于两者的比较,将会在分析之后给出。

ArrayList:先来看看最常用的ArrayList,它继承了AbstractList,实现了List, RandomAccess, Cloneable, Serializable四个接口。
常常听见ArrayList是用数组实现的,的确ArrayList中存储数据的结构就是一个对象数组,当new一个新的ArrayList,其实是new了一个长度为10(默认值)的对象数组。
其上的操作就是对数组的操作,只是更为简单而已。

    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

自动变长机制:
  印象很深的是ArrayList数组好用多了,之所以有这样的感觉,全都是因为ArrayList能够自动变长,而数组则是定长的,自动变长在编写程序时是极为方便的。
那么ArrayList是怎样实现自动变长的呢?OK,让我们先来分析add()方法

    public boolean add(Object o) {
       ensureCapacity(size + 1);  // Increments modCount!!
       elementData[size++] = o;
       return true;
    }
  可以看出,ensureCapacity()方法主要用来保证新增元素后不会越界,下面来看ensureCapacity

    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;
          elementData = new Object[newCapacity];
          System.arraycopy(oldData, 0, elementData, 0, size);
       }
    }
  此时就能看出自动变长是如何实现的了。当数组容量不够时先new一个新的数组,长度是原来的3/2,然后用System.arraycopy方法将数据从老数组copy到新数组,
由此可知,在改变数组长度时是需要开销的,就是说,变长是需要代价的。

迭代器: 
  使用List的迭代器时一定有所体会,当运行类似如下程序时,会抛出一个ConcurrentModificationException
  Iterator iterator = list.iterator();
  while(iterator.hasNext())
  {
    set.remove(iterator.next());
  }
  关于这点,JDK又是怎样实现的呢?思想就是将iterator看成一个“临界区”,只是不是多线程而是单个线程内部的“临界”。这里借助于一个modCount,定义如下:
  protected transient int modCount = 0;
  在迭代过程中不允许修改原始list的方法中(如add,remove)每次对modCount自增1,比如add方法中通过ensureCapacity方法让modCount++,而在调用迭代器时(实际
上是初始化迭代器时)将modCount赋值给expectedModCount
  int expectedModCount = modCount;
  每次调用迭代器的next()方法就会进行checkForComodification()检查是否expectedModCount已经不等于 modCount,如果不等,说明在迭代过程中已经通过remove()或add()等方法对原集合进行过修改,这是不允许的,所以 checkForComodification()抛出ConcurrentModificationException异常;如果相等,说明迭代过程中集合没有被修改过。
  为什么需要保证迭代中集合不能被修改呢?因为这样就不能保证集合能够被完全遍历到,因此,java的开发人员使用了这个比较巧妙的方法来解决这个问题。
 public Object next() {
     try {
        Object next = get(cursor);
        checkForComodification();
        lastRet = cursor++;
        return next;
     } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
     }
 

   final void checkForComodification() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
 }

序列化:
  ArrayList中最奇怪的就要数
  private transient Object elementData[];
中transient标志了,序列化最主要的目的就是将这个对象数组序列化,可偏偏它却加上了transient关键字阻止它的序列化,真是令人费解。
  查了些资料才知道,elementData中存放的都是对象的引用而不是真实对象,这就是说即使序列化也不能达到我们预想的效果,当你反序列化时这些引用 已经不指向原来的对象了,因此多了两个方法writeObject()和readObject()进行手工序列化或反序列化。
    private synchronized void writeObject  (java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        s.defaultWriteObject();

        // Write out array length
        s.writeInt(elementData.length);

        // Write out all elements in the proper order.
        for (int i=0; i
            s.writeObject(elementData[i]);
    }

    private synchronized void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in array length and allocate array
        int arrayLength = s.readInt();
        elementData = new Object[arrayLength];

        // Read in all elements in the proper order.
        for (int i=0; i
            elementData[i] = s.readObject();
    }


LinkedList:
  LinkedList的实现类似数据结构中的双向链表,每个结点是这样一个结构:
    private static class Entry {
       Object element;
       Entry next;
       Entry previous; 
 }
 
  构造一个带头节点的指针,请注意,这个链表是循环双向的,因此从头指针可以很容易找到尾节点。
 private transient Entry header = new Entry(null, null, null);
  LinkedList实现了一些特有的方法,比如 getFirst(),getLast(),addFirst(),addLast(),removeFirst(),removeLast(),看到这 个立马就想到队列,yes,完全正确,当你需要实现自己的queue时,LinkedList可是比较好的选择。

  比较下ArrayList、LinkedList、Vector:
  正如数组和链表的优劣一样,承袭了二者的ArrayList和LinkedList也是各有优劣。
  ArrayList适合查找,快速定位,一般需要使用变长的数组就非他莫数了。
  LinkedList适合频繁插入、删除,尤其是在表头尾的插入删除操作。
  Vector中,非常类似ArrayList,但是其中加入了同步锁,不同步的时候使用它是会降低效率的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值