`
taogebx
  • 浏览: 32628 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

集合初探--认识List

阅读更多
1. ArrayList

A) 底层数据结构



·本质是一个Object数组,存放的是对象引用序列。size代表元素个数。
·采用数组并通过算法保证了集合元素有序,允许重复的特性。

B) 构造方法

public ArrayList() {
    this(10);
} 

·创建一个ArrayList,默认大小为10。

C) 插入对象

·插入元素面临的一个重要的问题就是空间不够(这也是数组的最大弊端),如何扩容?ArrayList是通过一个公开的方法ensureCapacity(int minCapacity)来实现。
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);
    }
}

·先计算新的数组大小
    ·原数组的容量*1.5+1
    ·扩容后会检查是否能满足需要,若数组大小还不够,会直接扩容到需要的大小。这种情况主要发生在批量增加集合中的元素。
·再扩容(按照新的大小生成数组),同时拷贝原ArrayList中的元素。
    ·底层调用的是native方法:System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    ·频繁的扩容(移动元素)是耗性能的。若能明确List大小,给ArrayList设置合理的初始值是比较理想的。
·然后将新增元素插入到指定位置。
    ·批量增加集合中的元素会涉及两次拷贝。扩容会拷贝原数组元素,还会拷贝需要增加的集合中的元素。
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;
}


D)删除对象

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; // Let gc do its work
} 

·先通过for循环遍历(三种遍历方式:for循环,foreach,迭代iterator)找到删除元素。
·然后移动元素(被删除元素后的所有元素),再将最后一个元素设置为null,交给GC完成对象的回收。
·对于负数是不检查的,而是直接访问,直到抛出ArrayIndexOutOfBoundsException,参考 RangeCheck(int index)。
·删除元素,但是数组空间不会释放,可以调用trimToSize()缩小数组容量(ArrayList不会自动调用)
public void trimToSize() {
    modCount++;
    int oldCapacity = elementData.length;
    if (size < oldCapacity) {
        elementData = Arrays.copyOf(elementData, size);
    }
}


E)查找对象

·获取对象的位置:indexOf(Object o),从第一个元素往后查找;lastIndexOf(Object o),从最后一个元素往前查找。
·判断对象是否存在:contains(Object o),本质调用的是indexOf(Object o)。
·采用for循环遍历查找的方式。

2.LinkedList

A)底层数据结构




·底层采用双向循环链表结构实现。
·header:双向链表的头;Entry:包含三部分--对象数据,指向后一个Entry的引用,指向前一个Entry的引用。

B)构造方法

public LinkedList() {
    header.next = header.previous = header;
} 

private transient Entry<E> header = new Entry<E>(null, null, null);

Entry(E element, Entry<E> next, Entry<E> previous) {
    this.element = element;
    this.next = next;
    this.previous = previous;
}

·构造Entry(null,null,null):属性值全为null,同时赋值给header。
·构造方法仅仅生成双向链表结构:header.next = header.previous = header。

C)插入对象

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;
} 


·不用考虑扩容和数据复制(移动)。
·在header前插入,无须遍历。
·创建一个新对象,修改相邻元素属性。

D)删除对象

·遍历与匹配和ArrayList相似。
·删除元素:修改相邻元素属性,被删除元素属性置为null。无需复制元素。

E)查找对象 

·与ArrayList相似,不同的是LinkedList不支持下标index,在遍历的时候,临时记录一个index。
·获取对象的位置:indexOf(Object o),从第一个元素往后查找;lastIndexOf(Object o),从最后一个元素往前查找。
·判断对象是否存在:contains(Object o),本质调用的是indexOf(Object o)。
·采用for循环遍历查找的方式。

3.Vector

·继承AbstractList,实现List接口。与ArrayList相似。
·线程安全。
·初始大小为10。
·扩容策略与ArrayList不一样:
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);
    }
} 

    ·通过capacityIncrement控制:如果capacityIncrement>0,每次增加capacityIncrement。否则扩大为原来的两倍。

4.Stack

·后进先出:LIFO;入栈:push(E);出栈:pop(),返回最后一个元素并删除元素;peek(),返回最后一个元素但不删除。
·继承Vector,线程安全的,这使得stack也变得重量级。
·stack的父类不应该为Vector的,因为Vector的底层是数组(增删效率比较低),且Vector有get方法(意味着它可能访问到并不属于最后一个位置元素的其他元素,很不安全)。
·不考虑并发,怎么实现Stack的功能?封装LinkedList也许是不错的选择。
  • 大小: 6.3 KB
  • 大小: 6.1 KB
  • 大小: 11.5 KB
  • 大小: 15.9 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics