Java - 容器详解

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 一、ArrayList 长度可变数组,类似于c++ STL中的vector. 元素以线性方式连续存储,内部允许存放重复元素。 允许对元素进行随机的快速访问,但是向ArrayList中插入和删除元素的速度较慢。

 

一、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/

目录
相关文章
|
1月前
|
运维 负载均衡 Java
为什么 java 容器推荐使用 ExitOnOutOfMemoryError 而非 HeapDumpOnOutOfMemoryError ?
为什么 java 容器推荐使用 ExitOnOutOfMemoryError 而非 HeapDumpOnOutOfMemoryError ?
|
14天前
|
Java 容器
Java常用组件、容器与布局
Java常用组件、容器与布局
9 0
|
15天前
|
安全 Java API
Java并发 - J.U.C并发容器类 list、set、queue
Queue API 阻塞是通过 condition 来实现的,可参考 Java 并发 - Lock 接口 ArrayBlockingQueue 阻塞 LinkedBlockingQueue 阻塞 ArrayQueue 非阻塞 LinkedQueue 非阻塞
|
17天前
|
Java 开发者 容器
【Java】深入了解Spring容器的两个关键组件
【Java】深入了解Spring容器的两个关键组件
8 0
|
1月前
|
Java 容器
LeetCode题解-盛水最多的容器-Java
盛水最多的容器-Java
8 0
|
1月前
|
运维 Java 开发者
深入浅出:使用Docker容器化改善Java应用的部署与运维
在当今快速迭代的软件开发周期中,确保应用的一致性、可移植性与易于管理成为了开发与运维团队面临的重大挑战。本文旨在介绍如何通过Docker容器技术,有效地解决这些问题,特别是针对Java应用。我们将从Docker的基本概念出发,逐步深入到实际操作,展示如何将传统的Java应用容器化,以及这一过程如何帮助简化部署流程、提高应用的可靠性和可伸缩性。不同于常规的技术文章,本文试图以一种更加易于理解和实践的方式,让读者能够快速掌握容器化技术,并将其应用于日常的开发与运维工作中。
88 0
|
1月前
|
运维 Java Linux
深入解析:使用Docker容器化技术提升Java应用的部署效率
在快速迭代的软件开发周期中,如何保证应用的快速、一致和可靠部署成为了开发团队需要面对的重大挑战。本文将探讨如何利用Docker容器化技术,结合Java应用,实现高效、一致的部署流程。我们将从Docker的基本概念出发,详细介绍将Java应用容器化的步骤,包括创建Dockerfile、构建镜像以及运行容器等关键环节,并通过示例代码加以说明。此外,本文还将讨论在使用Docker部署Java应用时可能遇到的常见问题及其解决策略,旨在为读者提供一种提升部署效率、优化开发流程的有效方法。
295 2
|
1月前
|
Java 持续交付 虚拟化
深入浅出:使用Docker容器化改善Java应用的开发与部署流程
在快速迭代与持续集成的软件开发周期中,确保应用在各种环境中一致运行是一个挑战。本文介绍了如何利用Docker容器技术,来容器化Java应用,以实现环境一致性、简化配置和加速部署过程。我们将从Docker的基础知识开始,探讨其与传统虚拟机的区别,进而深入到如何创建Dockerfile,构建镜像,以及运行和管理容器。此外,文章还将涵盖使用Docker Compose来管理多容器应用的策略,以及如何利用容器化改善CI/CD流程。通过本文,读者将获得关于如何高效地利用Docker改善Java应用开发与部署流程的实践指导。
144 1
|
1月前
|
运维 Java 云计算
深入浅出:使用Docker容器化改进Java应用部署
在当前快速演变的软件开发领域,Docker作为一种开源的容器化技术,已经成为优化应用部署、实现快速交付和高效率运维的关键工具。本文将探讨如何利用Docker容器化技术来改进Java应用的部署流程。我们不仅会介绍Docker的基础知识,还会通过一个实际的Java应用示例,详细展示从创建Dockerfile到构建镜像,再到运行容器的整个过程。此外,文章还将探讨容器化带来的好处,如环境一致性、便捷的版本控制和简化的部署流程等,力求为读者提供一个清晰、易懂的指南,帮助他们在自己的项目中实现Docker容器化,从而提升开发和部署效率。
163 1
|
1月前
|
运维 Java 持续交付
深入浅出:使用Docker容器化改善Java应用的部署与运维
在当今快速发展的软件开发领域,持续集成与持续部署(CI/CD)已成为提高开发效率和软件质量的关键。本文将探讨如何利用Docker容器技术,实现Java应用的高效部署与运维。我们将从Docker的基本概念入手,详细介绍如何将传统的Java应用容器化,并通过实际案例展示容器化带来的便利性和高效性。此外,文章还将探讨Docker容器与传统虚拟机部署方式的对比,以及如何在实际项目中选择最适合的部署策略。通过本文,读者将能够深入理解Docker容器化技术,并学会如何在自己的Java项目中实施和优化。
220 1

相关产品

  • 容器镜像服务
  • 容器服务Kubernetes版