集合的区别

 

下面是Collection集合的说明:

Collection是最基本的集合接口,一个Collection代表一组Object,即Collection元素。一些Collection允许存储相同的元素而一些则不可以;一些能排序而一些不能。Jdk不提供直接继承自Collection接口的类,只提供ListSet两个子接口。

所有实现Collection接口的的类都必须提供两个标准的构造函数:一个无参的构造函数,用于创建一个空的Collection;一个有参的构造函数,用于复制一个Collection

关于如何遍历Collection集合元素:

不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,利用该迭代子即可逐一访问该Collection中的每一个元素。常用的做法如下:

Iterator it=collection.iterator();//获得一个迭代子

while(it.hashNext()){

Object o=it.next();//取得下一个元素

}

下面重点说说Collection接口派生的ListSet两个接口:

List接口是有序的Collection,使用此接口能够精确的控制每一个元素的插入位置。用户能够使用索引来获取元素,这一点类似于Array数组。ListSet不同的一点在于List允许有相同的元素,而Set不允许。

处理具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。实现List接口的常用类有LinkedListArrayListVectorStack是继承了Vector的类。

一下是List的几个常用子类的介绍:

LinkedList类:

LinkedList实现了List接口,它允许null元素。还有LinkedList提供额外的getremoveinsert方法在LinkedList的首部或尾部。这些操作使方法使得LinkedList可以被用作堆栈、队列、双向队列。使用LinkedList时需要注意的是:LinkedList是非同步的,如果多线程同时访问同一个LinkedList,必须自己实现访问同步。这里给出一个简单的解决方法是在创建List时构造一个同步的List,具体代码如下(参考):

List list=Collection.synchronizedList(new LinkedList(...));

ArrayList类:

ArrayList也继承List接口,ArrayList实现了可变大小的数组。它允许所有元素,包括null。每个ArrayList实例都有一个容量,即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,可在插入前调用ensureCapacity方法来增加ArrayList的容量以提高插入速率。和LinkedList一样,ArrayList也是非同步的,如果需要同步就用:

List list=Collection.synchronizedList(new ArrayList());

Vector类:

Vector非常类似ArrayList,但是Vector是同步的,由Vector创建的IteratorArrayList创建Iterator是同一个接口,但是由于Vector是同步的,当一个Iterator被创建而且正在被使用,而另一个线程改变了Vector的状态(比如:向Vector添加一个元素),这时候调用Iterator的方法将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack类:

Stack继承Vector而不是List,实现了一个先进后出的堆栈。Stack提供了5个额外的方法使得Vector得以被当做堆栈来使用。基本的push元素进栈和pop栈方法,还有peek方法取得栈顶元素,empty方法判断堆栈是否为空,search方法用于寻找某元素在堆栈中的位置。Stack创建后是空栈。

以下是Set接口的介绍:

Set是一种不包含重复元素的Collection,即任意的两个元素e1e2都有如下结果

e1.equals(e2)=false, Set最多只有一个null元素。由于Set不能含有重复元素,因此Set的构造函数有一个约束条件传入的Collection参数不能包含重复的元素。在使用Set操作可变对象时,必须保证object1.equals(object2)=false;否则可能导致一些问题。

以下是Map接口说明:

这里要特别注明的是Map并没有继承Collection接口,Map提供keyvalue的映射。一个Map中的key是不能相同的,每个key只能对应一个valueMap接口提供三种集合视图。Map内容可以被当做一组key-value映射。实现Map接口的类有HashTableHashMapWeakHashMap

HashTable类:

HashTable实现了Map接口,实现一个key-value映射的哈希表,任何非空的对象都可以作为key或者value。添加数据使用put(key,value),取出数据使用get(key),这两个操作的时间开销均为常数。Hashtable通过initial capacityload factor两个参数调整性能。增大load factor可以节省空间,但相应的查找时间将增大,会影响getput的操作。由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode()方法和equals()方法。由于hashCode()方法和equals()方法都继承自根类Object,如果用自定义的类当做key的话,要注意,按照散列函数的定义,如果两个对象相同,即object1.equals(object2)=true;则它们的hashCode必须相同,如果两个对象不相同,则它们的hashCode不一定不同。如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好hashCode()方法,能加快哈希表的操作效率。HashTable是同步的,因此它是线程安全的。


HashMap类:

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

此实现假定哈希函数将元素正确分布在各桶之间,可为基本操作(get put)提供稳定的性能。迭代集合视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)的和成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。

通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地降低 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

注意,此实现不是同步的。如果多个线程同时访问此映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:

Map m = Collections.synchronizedMap(new HashMap(...));

由所有此类的“集合视图方法”所返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

WeakHashMap类:

弱键 实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。丢弃某个键时,其条目从映射中有效地移除,因此,该类的行为与其他的 Map 实现有所不同。

null 值和 null 键都被支持。该类具有与 HashMap 类相似的性能特征,并具有相同的效能参数初始容量加载因子

像大多数集合类一样,该类是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap

该类主要与这样的键对象一起使用,其 equals 方法使用 == 运算符来测试对象标识。一旦这种键被丢弃,就永远无法再创建了,所以,过段时间后在 WeakHashMap 中查找此键是不可能的,不必对其项已移除而感到惊讶。该类十分适合与 equals 方法不是基于对象标识的键对象一起使用,比如,String 实例。然而,对于这种可重新创建的键对象,键若丢弃,就自动移除 WeakHashMap 条目,这种表现令人疑惑。

WeakHashMap 类的行为部分取决于垃圾回收器的动作,所以,几个常见的(虽然不是必需的)Map 常量不支持此类。因为垃圾回收器在任何时候都可能丢弃键,WeakHashMap 就像是一个被悄悄移除条目的未知线程。特别地,即使对 WeakHashMap 实例进行同步,并且没有调用任何赋值方法,在一段时间后 size 方法也可能返回较小的值,对于 isEmpty 方法,返回 false,然后返回 true,对于给定的键,containsKey 方法返回 true 然后返回 false,对于给定的键,get 方法返回一个值,但接着返回 null,对于以前出现在映射中的键,put 方法返回 null,而 remove 方法返回 false,对于键集、值集、项集进行的检查,生成的元素数量越来越少。

WeakHashMap 中的每个键对象间接地存储为一个弱引用的指示对象。因此,不管是在映射内还是在映射之外,只有在垃圾回收器清除某个键的弱引用之后,该键才会自动移除。

实现注意事项:WeakHashMap 中的值对象由普通的强引用保持。因此应该小心谨慎,确保值对象不会直接或间接地强引用其自身的键,因为这会阻止键的丢弃。注意,值对象可以通过 WeakHashMap 本身间接引用其对应的键;这就是说,某个值对象可能强引用某个其他的键对象,而与该键对象相关联的值对象转而强引用第一个值对象的键。处理此问题的一种方法是,在插入前将值自身包装在 WeakReferences 中,如:m.put(key, new WeakReference(value)),然后,分别用 get 进行解包。

该类所有“collection 视图方法”返回的迭代器均是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 removeadd 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。

最后简单总结:

ArrayList是基于动态数组的,LinkedList是基于链表的;因此查询和修改较快的是ArrayList,插入和删除较快的是LinkedList。因为ArrayList插入或删除操作时需要移动元素,而LinkedList查询修改时需要移动指针。

ArrayList和Vector都是基于动态数组的,如果插入的元素超出了原有的容量,ArrayList和Vector都会自动增加容量,但是ArrayList默认增加原来的50%,而Vector默认增加原来的一倍也就是100%,因此如果要在集合中保存大量的数据,则使用Vector。还有就是Vector是线程同步安全的。而ArrayList是非同步的,线程不安全的,因此,在单线程下用ArrayList比较好,在多线程的条件下,用Vector性能较好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值