集合初探--认识List

[size=large][color=blue][b]1. ArrayList[/b][/color][/size]

A) 底层数据结构

[img]http://dl.iteye.com/upload/attachment/445196/3587517b-f342-3582-b265-62cc0ed8b5f0.jpg[/img][img]http://dl.iteye.com/upload/attachment/445198/19b87698-c21f-35bb-bdb8-086639a63615.jpg[/img]

·本质是一个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循环遍历查找的方式。

[size=large][color=blue][b]2.LinkedList[/b][/color][/size]

A)底层数据结构

[img]http://dl.iteye.com/upload/attachment/445885/58fddb5c-faec-35ff-b7b2-76aa86ffac9f.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/445887/40bbbf9d-b9d9-3ce0-bb2c-ce4cc14e5848.jpg[/img]

·底层采用双向循环链表结构实现。
·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循环遍历查找的方式。

[size=large][color=blue][b]3.Vector[/b][/color][/size]

·继承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。否则扩大为原来的两倍。

[size=large][color=blue][b]4.Stack[/b][/color][/size]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值