Java容器:
Collection
├List 接口
│├LinkedList 链表
│├ArrayList 顺序结构动态数组类
│└Vector 向量
│ └Stack 栈
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
首先介绍Collection接口:
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。
Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
对应list的用法分为:
对数据进行操作(增,删,改,查);对数据存储后遍历,显示,这两大类应用。
数据定位:
1.List接口提供了两种搜索指定对象的方法。从性能的观点来看,应该小心使用这些方法。
2.为了提高效率,通常我们对list中元素排好序,进行折半查找,通过获取子列表缩小查找范围,,子列表的获取。实现时注意列表中存在重复元素,对获取边界值的影响。
对于元素对象需要重写equals
(Object o)
和hashCode()方法 实现自定义的对象相等,在比较元素相等时,我们应先判断元素的hashCode值是否相等。
对于判断列表是否相等(元素的值和元素的顺序),也可以使用列表的hashCode方法来提高判读的效率。
利用Collections也可以实现对list元素进行排序及其查找,列表中的所有元素都必须实现Comparable接口 。
3.对于支持范型的jdk版本来说:
|
toArray(T[] a) |
indexOf
(Object o)
返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
lastIndexOf
(Object o)
返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
get
(int index)
返回列表中指定位置的元素。
List<E> |
subList(int fromIndex, int toIndex) |
inthashCode() 返回列表的哈希码值。列表的哈希码定义为以下计算的结果: int hashCode = 1; Iterator<E> i = list.iterator(); while (i.hasNext()) { E obj = i.next(); hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); }
这确保了 list1.equals(list2)意味着对于任何两个列表list1 和 list2 而言,可实现 list1.hashCode()==list2.hashCode(),正如 Object.hashCode()的常规协定所要求的。 |
增:
boolean |
add(E e) |
void |
add(int index,E element) |
删:删除单个,一组,全部元素。
E |
remove(int index) |
|
boolean |
remove(Object o) |
|
boolean |
removeAll(Collection<?> c) |
|
boolean |
retainAll(Collection<?> c) |
|
void |
clear() |
|
改:
E |
set(int index,E element) |
遍历:
iterator
()
和ListIterator的差别?
Iterator<E> |
iterator() |
ListIterator<E> |
listIterator() |
ListIterator<E> |
listIterator(int index) |
Object[] |
toArray() |
|
|
toArray(T[] a) |
针对list接口的实现类:LinkedList ,ArrayList,Vector,Stack各自都增加了那些方法,实际使用时如何选择?
首先来看List接口的链接列表实现LinkedList:
所有已实现的接口:
Serializable,Cloneable,Iterable<E>, Collection<E>, Deque<E>, List<E>, Queue<E>
实现所有可选的列表操作,并且允许所有元素(包括null)。除了实现List接口外,LinkedList类还为在列表的开头及结尾 get、remove和insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque接口,为add、poll提供先进先出队列操作,以及其他堆栈和双端队列操作。
再来看List接口的数组列表实现 ArrayList:
所有已实现的接口:
Serializable,Cloneable,Iterable<E>, Collection<E>, List<E>, RandomAccess
List接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括null在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于Vector类,除了此类是不同步的。)
size、isEmpty、get、set、iterator和listIterator 操作都以固定时间运行。add操作以分摊的固定时间 运行,也就是说,添加 n个元素需要 O(n)时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList实现的常数因子相比,此实现的常数因子较低。
每个 ArrayList实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。
在添加大量元素前,应用程序可以使用 ensureCapacity操作来增加ArrayList 实例的容量。这可以减少递增式再分配的数量。
关于arrayList的容量和元素个数,
trimToSize()
将此 ArrayList实例的容量调整为列表的当前大小。
int |
size() |
void |
ensureCapacity(int minCapacity) |
总结:
1. 对于频繁使用插入与删除操作使用linkedlist是个不错的选择,对于经常进行索引操作则arrylist较好;从元素个数来判断,对于知道元素个数,用来遍历显示时,我们用arrayList更高效。对于将列表用作堆栈、队列或双端队列操作时,选择linkedlist。实际应用:从数据库返回结果集,显示在页面时,arrayList更合适。
2. Linkedlist,arrylist,此实现不是同步的。如果多个线程同时访问一个ArrayList或链接列表实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedList
方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(...));
List list = Collections.synchronizedList(new LinkedList(...));
vector的Vector中的所有方法前面都有一个synchronized关键字做修饰,是线程安全的。
3. Linkedlist,arrylist,vector类的iterator和 listIterator方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的remove或 add方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出ConcurrentModificationException
。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。因此实际应用是,先通过iterator和listIterator遍历数组,确定元素后再进行增,删时,应用迭代器自身的方法来,修改是否类似?个人觉得也不能在迭代器中修改。如果修改,重新生成迭代器。