Java - 容器详解

  1. 云栖社区>
  2. 博客>
  3. 正文

Java - 容器详解

北岛知寒 2016-03-16 20:56:00 浏览1417
展开阅读全文

 

一、ArrayList

长度可变数组,类似于c++ STL中的vector.

元素以线性方式连续存储,内部允许存放重复元素。

允许对元素进行随机的快速访问,但是向ArrayList中插入和删除元素的速度较慢

ArrayList是非线程安全的,若要成为线程安全,可以使用:List list=Collections.synchronizedList(new ArrayList());

数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。

这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张

当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。

二、LinkedList

内部采用双向循环链表实现,类似于c++ STL中的list.

插入和删除元素的速度较快,随机访问的速度较慢.

LinkedList单独具有addFirst(),addLast(),getFirst(),getLast(),removeFirst()和removeLast()方法.

这些方法使得它可以作为堆栈、队列和双向队列来使用.

LinkedList也是非线程安全的.

三、HashMap

基于哈希表的Map接口实现,类似于c++ STL中的unordered_map.

元素是无序的.

采用拉链法来处理哈希冲突,注意:链表中存储的仍然是key值,而非value,对于同一个key,value的新值会替换旧值.

当哈希表中的条目数超过了加载因子与当前容量的乘积时,则要对该哈希表进行rehash操作(重建内部数据结构),将哈希的桶数量扩大两倍左右.

HashMap是基于HashCode的,HashMap也是非线程安全的,若要线程安全,可用:Map m=Collections.synchronizedMap(new HashMap());

自己实现,最好使用局部锁:把HashMap进行拆分,拆分成了多个独立的块,这样在高并发的情况下减少了锁冲突的可能,增加了并发性.

四、TreeMap

基于红黑树,类似于c++ STL中的map.

使用了SortedMap接口.

确保key键是有序的,无重复的.

也是非线程安全的.

五、HashSet

基于Hash表的Set接口实现,类似于c++ STL中的unordered_set,采用hash散列存储.

元素是无序的,无重复的.

也是非线程安全的.

六、TreeSet

基于红黑树实现,类似于c++ STL中的set.

TreeSet 底层实际使用的存储容器就是TreeMap.

元素是有序的,无重复的.

也是非线程安全的.

七、总结

Java中的容器大多在c++ STL中都有实现,实现方法和使用方法都大同小异.

在Java容器中,只有HashTable和Vector是线程安全(同步)的,同步实现的方法是:

将状态封闭起来,并对每个共有方法进行同步,是的每次只有一个线程能访问容器状态,并发性较差.

 

------------------------------------------------------------------------------------------

 

什么是Map.

  在数组中我们是通过数组下标来对其内容索引的,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value.这就是我们平时说的键值对.

  HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的).

  HashMap 非线程安全 TreeMap 非线程安全

线程安全

  在Java里,线程安全一般体现在两个方面:

  1. 多个thread对同一个java实例的访问(read和modify)不会相互干扰,它主要体现在关键字synchronized.

  如ArrayList和Vector,HashMap和Hashtable(后者每个方法前都有synchronized关键字)。

  如果你在interator一个List对象时,其它线程remove一个element,问题就出现了.

  2. 每个线程都有自己的字段,而不会在多个线程之间共享. 它主要体现在java.lang.ThreadLocal类,而没有Java关键字支持,如像static. transient那样.

1.AbstractMap抽象类和SortedMap接口

  AbstractMap抽象类:(HashMap继承AbstractMap)覆盖了equals()和hashCode()方法以确保两个相等映射返回相同的哈希码. 如果两个映射大小相等. 包含同样的键且每个键在这两个映射中对应的值都相同,则这两个映射相等. 映射的哈希码是映射元素哈希码的总和,其中每个元素是Map.Entry接口的一个实现. 因此,不论映射内部顺序如何,两个相等映射会报告相同的哈希码.

  SortedMap接口:(TreeMap继承自SortedMap)它用来保持键的有序顺序. SortedMap接口为映像的视图(子集),包括两个端点提供了访问方法. 除了排序是作用于映射的键以外,处理SortedMap和处理SortedSet一样. 添加到SortedMap实现类的元素必须实现Comparable接口,否则您必须给它的构造函数提供一个Comparator接口的实现. TreeMap类是它的唯一一份实现.

2.两种常规Map实现

  HashMap:基于哈希表实现. 使用HashMap要求添加的键类明确定义了hashCode()和equals().

   可以重写hashCode()和equals()。为了优化HashMap空间的使用,您可以调优初始容量和负载因子.

  • HashMap(): 构建一个空的哈希映像
  • HashMap(Map m): 构建一个哈希映像,并且添加映像m的所有映射
  • HashMap(int initialCapacity): 构建一个拥有特定容量的空的哈希映像
  • HashMap(int initialCapacity, float loadFactor): 构建一个拥有特定容量和加载因子的空的哈希映像

  TreeMap:基于红黑树实现. TreeMap没有调优选项,因为该树总处于平衡状态.

  • TreeMap():构建一个空的映像树
  • TreeMap(Map m): 构建一个映像树,并且添加映像m中所有元素
  • TreeMap(Comparator c): 构建一个映像树,并且使用特定的比较器对关键字进行排序
  • TreeMap(SortedMap s): 构建一个映像树,添加映像树s中所有映射,并且使用与有序映像s相同的比较器排序

3.两种常规Map性能

  •   HashMap:适用于在Map中插入. 删除和定位元素.
  •   Treemap:适用于按自然顺序或自定义顺序遍历键(key).

4.总结

  HashMap通常比TreeMap快一点(树和哈希表的数据结构使然),建议多使用HashMap,在需要排序的Map时候才用TreeMap.

 

------------------------------------------------------------------------------------------

 

 

 

一、Collection:存放独立元素

Collection中的接口都是可选操作,事实上现类 并不一定实现了其全部接口,这是为了防止“接口爆炸”。

最常见的Unsupported Operation都源自于背后由固定尺寸的数据结构支持的容器,比方使用ArrayList.asList将数组转换成List的时候,就会得到这种容器。

 

 (1)CollectionList

List较之Collection添加了非常多额外的接口,特别是LinkedList。

 

长处

缺点

保存元素的顺序

应用

ArrayList            

随机訪问速度快,内部使用数组实现。

 

迭代,插入和删除元素慢,

尤其是当List尺寸比較大的时候。

插入顺序

可变长数组

LinkedList

迭代(顺序訪问经过优化),插入、删除都非常快

内部使用双向链表实现

随机訪问速度慢

插入顺序

顺序訪问, 批量插入删除元素的场合

 

(2)CollectionSet

存入Set的每个元素都必须是唯一的,由于Set不保存重复元素。

可是存入Set的元素必须要定义equals()方法以确保对象的惟一性。

另外Set与Collection有着全然同样的接口。

Set并不保证维护元素的次序,所以须要注意。

Set不包括随机訪问的get方法。由于Set是自己维护内部顺序的,不须要随机訪问。

 

长处

保存元素的顺序

要求

HashSet

为快速查找设计

散列存储

必须定义hashCode()方法

LinkedHashSet                   

和HashSet一样的查询速度,

可是插入要比HashSet慢一些,

由于它通过维护链表形式维护元素。

 

使用链表维护元素顺序(插入顺序)

必须定义hashCode()方法

TreeSet

保存有序的Set,底层通过TreeMap来实现的

依照排序顺序维护元素

必须实现Comparable接口(包括compareTo方法)

注意,假设没有特别的限制,HashSet就应该是你的默认选择,由于它对速度进行了优化。

 

(3)CollectionQueue

 

特点

保存元素的顺序

LinkedList

LinkedList除了普通List之外,

还加入了<队列、栈、双向队列>三种数据结构的方法。

尤其是模拟Queue的时候在两端插入删除元素非常快(经过了优化)。

 

插入的顺序

PriorityQueue   

依照排序顺序取出元素,所以要求必须实现Comparable接口。

排序顺序

 

二、Map:存放键-值对

为了保证Map中的唯一性,不论什么“键”都须要有一个equals()方法,推断当前键是否与表中的键同样。

                                                                      

特点

保存元素的顺序              

要求

HashMap

Map基于散列存储。插入和查询“键值对”的开销是固定的。

 

散列存储

存入的键须要具备hashCode()方法,当然,返回的标识不一定要唯一

LinkedHashMap

为了提快速度散列了全部元素,插入查询仅仅比HashMap慢一点点,由于它在维护散列数据结构的同一时候还要维护链表(插入顺序)。 可是迭代訪问的时候更快。由于内部使用链表维护次序。

 

插入顺序

同样须要键实现hashCode()方法

TreeMap

Map基于红黑树的实现。所以所得的结果是经过排序的。

红黑树

为了排序。必须实现Comparable接口。

其它为解决特殊问题设计的Map还有IdentityHashMap。WeakHashMap,ConcurrentHashMap等。

 

实验证明。除了IdentityHashMap之外的其它全部Map,随着Map的尺寸变大,插入会明显变慢。可是查找的代价小非常多。我推測这是由于每次插入都要通过equals方法来确保键值的唯一性导致的,只是Map最经常使用的操作是查询操作,所以情况还比較乐观。

 

总结:

(1)要想让你的容器类型安全,须要使用泛型(否则编译器同意你向容器中插入各种不同类型,仅仅要是Object就可以);

(2)创建容器的时候尽量将其向上转型为接口,像这样:

  List<Apple> apples = newArrayList<Apple>();

   使用接口的目的在于当你决定改动你的实现的时候。仅仅须要在创建的地方改动它就能够了。

(3)Java有大量的用于容器的卓越的方法,他们被封装到java.util.Collections类中,全部都是static方法。

比方Collections类中有unmodifiableMap/List/Set()方法设置Collection和Map为不可改动。还有synchronizedCollection/List/Set/Map等方法同步整个容器。

另外排序,填充,逆置,最大最小值等非常多方法也能够找到。

 

--------------------------------------------------------- End.

转载请注明:http://www.cnblogs.com/crazyacking/

网友评论

登录后评论
0/500
评论
北岛知寒
+ 关注