本文关键词:html
java集合框架 框架设计理念 容器 继承层级结构 继承图 集合框架中的抽象类 主要的实现类 实现类特性 集合框架分类 集合框架并发包 并发实现类java
由一个或多个肯定的元素所构成的总体叫作集合。程序员
容器用来包装或装载物品的贮存器 (如箱、罐、坛)或者成形或柔软不成形的包覆材料。算法
在Java中的Collection框架,有的人叫作集合有的叫作容器,无论怎么叫基本上也离不开"把元素装起来"这个本质.编程
咱们的世界里面丰富多彩,有各类各样的事物,不少事物都会有他的容器api
人的生活天然也离不开各类容器,喝水须要杯子,吃饭须要碗,煮汤须要锅,这都是容器;数组
容器有各类各样的形状,也有各类各样的特性;安全
好比茶杯有的有盖子,有的没有盖子,水壶能够从壶嘴往外倒水等数据结构
java是面向对象的语言,万事万物皆是对象,纵然有着千姿百态的各类不一样类型多线程
因此想要在java对象中更加畅快的使用对象,天然也是须要容器的;
按照容器的概念,数组也是一种容器,能够用于存放一个或者多个元素;
但是,数组只能保存同一种类型的元素,并且长度是固定的;
人们天然但愿能够有一种容器可以保存各类不一样的类型的元素,而且长度是不固定的;
这也是集合框架设计的初衷;
提供一种能够保存多种类型元素,而且长度不受限制的容器,来更加方便的保存对象;
因此java中的容器也就是java世界里面承装对象的器皿.
容器根本属性在于存/取,以及一些其余的附加的操做.
容器内部有其摆放形式:排成一行仍是扔到一堆?
也有他的存取顺序:先进先出仍是先进后出的被压倒最下面?
这是抽象的描述
对应到计算机科学的世界里面,那便是数据结构与算法的描述
数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成
数据结构中有线性结构,树形结构等,形式有队列 栈 键值对 等
至此,能够这么理解编程语言中的集合框架:
集合框架目的就只是为了盛装对象,操做对象
本质就是Java语言,针对于容器这一律念,数据结构与算法的描述实现.
更直白的说,也就只是数据结构与算法,java只是一个表现形式
好比LinkedList 他就是java语言对于双向链表的一种描述,若是你懂双向链表的原理,而且懂得java的语法,你也能够实现一个LinkedList
不过,选取哪些数据结构,使用哪些算法,继承层级如何安排,这是java本身的特色;
固然,并非说你用Java编写一个双向链表就是写出来集合框架了Java是面向对象的语言,面向对象的三大基础特征,封装继承多态嘛想要给一门编程语言提供一个集合框架,天然不是写几个算法数据结构这么简单的事情Java中的集合框架是自顶而下设计的如同全部的对象的祖宗都是Object同样集合框架天然也是有祖宗的,那就是Collection 这就表示集合 ,在Java中用来存储元素的容器
不过也还有另一派,叫作Map ,如官方文档中描述的那样,Map并不算是集合,只不过是一种操做数据的结构而已可是Map也提供了相似集合似的存取元素,元素操做等功能广义上按照咱们以前说的集合/容器的概念去理解的话,天然他也能够算得上是Java集合的一份子因此通常都是把Map和Collection统称为Java的集合体系的鉴于Java语言的特性,集合体系中这些用于刻画家族脸谱的东西,天然都是接口,除非特别指明,所提到的类型均为接口
Collection中是一组独立的元素而Map中则是一组成对的键值对元素
一组独立的元素,Collection,中又能够按照有序的列表和无序的集,这就是List 和Set固然还有Queue,队列其实话说回来,为什么Queue直接继承自Collection?有序和无序的并集不就已是总体了么队列不也算是一种特殊的List么,的确队列是一种特殊的List,并且,经常使用的实现类LinkedList
.....implements List<E>, Deque<E>..... 而且其中 interface Deque<E> extends Queue<E>
因此说,队列逻辑思惟意义上就是列表中比较特殊的一种,只不过他的特殊性比较多
因此在实现代码的时候把它单独拿出来直接继承自Collection 做为一种大的分类
也我以为也并无什么太大的违和感,我的理解,欢迎指正
如今咱们已经"高屋建瓴"的把集合Collection分为了 List Set Queue这三种,再加上刚才说到的Map
四大天王已经诞生了
Set和Map自己是无序的,在此基础上又增长了排序的概念
因此家族里面又多出来sortedSet 和 sortedMap
既然有了排序的概念,那么在此之上继续增长个可搜索的功能也没什么好奇怪的
也就是
NavigableSet 和 NavigableMap
不得不说,美国人英语真好
Queue队列中又分为:
双端队列Deque (double ended queue)
因此主要的接口是这些:
Collection
|---List
|---Set
|---sortedSet
|---NavigableSet
|---Queue
|---Deque
|---Map
|---sortedMap
|---NavigableMap
至此,对于java集合来讲,意识形态层面的设计已经完成.
一人心难如万人意,集合框架设计者也明白这个道理
天然知道提供的实现类并不能知足全部人需求,天然有人想要本身实现,
若是从头写来一个天然是代价巨大,考虑到这点,集合框架提供了很多的抽象类,抽象类实现了大部分通用的方法
你想要实现,只须要继承抽象类,而且实现必要的几个方法便可
固然,集合的设计自己也是这个思路,一箭双雕,本身写的这么方便的东西,没道理不用;
抽象类大多数以Abs开头的
AbstractCollection:
提供了Collection的主要实现
Collection 下的大多数子类都继承 AbstractCollection ,好比 List 的实现类, Set的实现类。
AbstractList
List接口的骨架实现,最大限度地减小实现由“随机访问”数据存储(如数组)所支持的接口所需的工做量。
对于顺序访问数据(如连接列表),应该优先使用AbstractSequentialList。
AbstractSequentialList
List接口的骨架实现,以最大限度地减小实现由“顺序访问”数据存储(如连接列表)支持的接口所需的工做量。
对于随机访问数据(如数组),应优先使用AbstractList。
AbstractSet
提供了Set接口的骨架实现,不会覆盖AbstractCollection类中的任何实现。它只是增长了equals和hashCode的实现。
经过扩展此类来实现集合的过程与经过扩展AbstractCollection来实现集合的过程相同
不一样之处在于此类的全部子类中的全部方法和构造函数都必须遵照Set接口施加的额外约束(例如,添加方法不得容许将一个对象的多个实例添加到一个集合中)。
AbstractQueue
提供了一些Queue操做的骨架实现
当基类实现不容许空元素时,此类中的实现适用。
方法add,remove和element分别基于offer,poll和peek,可是会抛出异常而不是经过false或null返回来指示失败。
扩展此类的任何Queue实现类至少也须要定义方法Queue.offer(E),该方法不容许插入空元素
以及方法Queue.peek(),Queue.poll(),Collection.size()和Collection.iterator()。
一般,其余方法也将被覆盖。若是这些要求不能知足,请考虑派生AbstractCollection的子类。
AbstractMap
Map接口的骨架实现
要实现一个不可修改的映射,程序员只须要扩展这个类并为entrySet方法提供一个实现,该方法返回Map映射的set-view。
一般,返回的集合是AbstractSet的一个实现。并且通常是内部类的形式
这个集合不该该支持add或remove方法,它的迭代器不该该支持remove方法。
EnumSet
用于枚举类型的专用Set实现
主要的实现类有:
Collection下面:
其中List的实现类主要是:
(1)ArrayList
List接口的可调整大小数组实现。
实现List接口中全部的可选操做,并容许任意元素,包括null。
除了实现List接口以外,该类还提供了一些方法来控制用于内部存储列表的数组大小。(这个类大体至关于Vector,除了它是不一样步的。)
此实现是不一样步。
(2)LinkedList
List和Deque接口的双端链表实现。实现List接口全部的可选操做,并容许任意元素(包括null)。
此实现是不一样步。
(3)Vector
Vector类实现了一个可增加的对象数组。
像数组同样,它包含可使用整数索引访问的组件。
不一样于数组的是,Vector的大小可根据须要增大或减少,以适应在建立Vector以后添加和移除项目。
同步的
(4)Stack
Stack类表示后进先出(LIFO)对象堆栈。
它使用五个操做来扩展类Vector,这样子能够将一个Vector视为一个堆栈。
提供了:
一般的推送和弹出操做,
以及一种方法来查看堆栈中的顶层项目,
一种方法来测试堆栈是否为空,
以及一种方法来搜索堆栈中的项目并发现它有多远是从顶部。
当第一次建立堆栈时,它不包含任何元素。
Deque接口及其实现提供了更完整和一致的LIFO堆栈操做集,应优先使用此类。例如:
Deque<Integer> stack = new ArrayDeque<Integer>();
Set下面主要是:
(1)HashSet
这个类实现了Set接口
由一个哈希表(其实是一个HashMap实例)支持。
它对集合的迭代次序没有任何保证;
特别是,它不能保证顺序会随着时间的推移保持不变。这个类容许null元素。
HashSet应该是你在没有特殊要求下的默认选择
这个类为基本操做(添加,删除,包含和大小)提供了恒定的时间性能,假设散列函数在桶之间正确地分散元素。
迭代此集合须要的时间与HashSet实例的大小(元素数量)加上支持HashMap实例的“容量”(桶的数量)的总和成正比。
所以,若是迭代性能很重要,不要将初始容量设置得过高(或者负载因子过低)是很是重要的。
非同步的
(2)TreeSet
基于TreeMap的NavigableSet实现。
这些元素使用它们的天然顺序或者在建立集合时提供的比较器进行排序,具体取决于使用哪一个构造函数。
这个实现保证:基本操做(添加,移除和包含)的时间复杂度为 log(n)非同步的
(3)LinkedHashSet
Set接口的哈希表和链表实现,具备可预测的迭代顺序。
这个实现与HashSet的不一样之处在于它保持了一个双向链表,它贯穿其全部条目。
此连接列表定义迭代排序,即元素插入到集合中的顺序(插入顺序)。
请注意,若是元素从新插入到集合中,则插入顺序不受影响。
(若是s.contains(e)在调用以前当即返回true,则调用s.add(e)时,将元素e从新插入到集合s中。)
非同步的
HashSet的性能老是比TreeSet好,特别是添加和查询元素
TreeSet存在的惟一缘由就是能够维持元素的排序状态,因此,只有当须要一个排好序的Set时
才应该使用TreeSet
对于插入操做 LinkedHashSet比HashSet的代价要高
Queue的:
(1)ArrayDeque
Deque接口的可调整大小的实现。
Array deques没有容量限制;根据使用状况动态增加.
它们不是线程安全的
在没有外部同步的状况下,它们不支持多线程的并发访问。
禁止使用空元素
当用做堆栈时,该类可能比Stack快,而且在用做队列时比LinkedList快。
这个类的迭代器方法返回的迭代器是快速失败机制的,会抛异常
ConcurrentModificationException
.
(2)PriorityQueue
基于优先级堆的无限优先级队列
优先级队列的元素根据其天然排序或队列构建时提供的比较器进行排序,具体取决于使用哪一个构造函数
优先级队列不容许空元素。依赖于天然顺序的优先级队列也不容许插入非可比对象(这样作可能致使ClassCastException)。
非同步的
优先级队列是无界的,但具备控制用于存储队列中元素的数组大小的内部容量。
它老是至少与队列大小同样大。随着元素被添加到优先级队列中,其容量会自动增长。
Map下面:
(1)HashMap
基于哈希表的Map接口实现
该实现提供了全部可选的Map操做,并容许使用空值和空键
(HashMap类与Hashtable大体相同,只是它不一样步并容许空值。)
这个类不能保证顺序;并且,它不能保证顺序会随着时间的推移保持不变。
非同步的
(2)Hashtable
这个类实现了一个哈希表,它将键映射到值。任何非空对象均可以用做键或值。
要成功地从哈希表存储和检索对象,用做键的对象必须实现hashCode方法和equals方法。
一个Hashtable的实例有两个影响其性能的参数:初始容量和负载因子
容量是哈希表中桶的数量,初始容量就是哈希表建立时的容量。
一般,默认的加载因子(.75)在时间和空间成本之间提供了一个很好的折衷。
从Java 2平台v1.2开始,该类被改型为实现Map接口,使其成为Java集合框架的成员。
与新的集合实现不一样,Hashtable是同步的。
若是不须要线程安全的实现,建议使用HashMap来代替Hashtable。
若是须要线程安全的高度并行实现,则建议使用ConcurrentHashMap代替Hashtable。
(3)LinkedHashMap
Map 接口的哈希表和连接列表实现,具备可预知的迭代顺序。
此实现与 HashMap 的不一样之处在于,后者维护着一个运行于全部条目的双重连接列表。
此连接列表定义了迭代顺序,该迭代顺序一般就是将键插入到映射中的顺序(插入顺序)。
注意,若是在映射中从新插入 键,则插入顺序不受影响。
(若是在调用 m.put(k, v) 前 m.containsKey(k) 返回了 true,则调用时会将键 k 从新插入到映射 m 中。)
(3)TreeMap
基于红黑树(Red-Black tree)的 NavigableMap 实现。
该映射根据其键的天然顺序进行排序,或者根据建立映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
此实现为 containsKey、get、put 和 remove 操做提供受保证的 log(n) 时间开销。
这些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改编。
此实现不是同步的
(4)WeakHashMap
以弱键 实现的基于哈希表的 Map。
在 WeakHashMap 中,当某个键再也不正常使用时,将自动移除其条目。
更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,而后被回收。
丢弃某个键时,其条目从映射中有效地移除,所以,该类的行为与其余的 Map 实现有所不一样。
null 值和 null 键都被支持。该类具备与 HashMap 类类似的性能特征,并具备相同的效能参数初始容量 和加载因子。
像大多数 collection 类同样,该类是不一样步的。可使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。
(5)EnumMap
与枚举类型键一块儿使用的专用 Map 实现。
枚举映射中全部键都必须来自单个枚举类型,该枚举类型在建立映射时显式或隐式地指定。
枚举映射在内部表示为数组。此表示形式很是紧凑且高效。
(6)IdentityHashMap
此类利用哈希表实现 Map 接口,比较键(和值)时使用引用相等性代替对象相等性。
换句话说,在 IdentityHashMap 中,当且仅当 (k1==k2) 时,才认为两个键 k1 和 k2 相等
(在正常 Map 实现(如 HashMap)中,当且仅当知足下列条件时才认为两个键 k1 和 k2 相等:(k1==null ? k2==null : e1.equals(e2)))。
此类不是 通用 Map 实现!
此类实现 Map 接口时,它有意违反 Map 的常规协定,该协定在比较对象时强制使用 equals 方法。此类设计仅用于其中须要引用相等性语义的罕见状况。
基于并发编程的特性
又延伸出来下面这些接口:
java.util.concurrent包中
队列Queue中:
阻塞队列 BlockingQueue--生产者向队列添加元素但队列已满时,生产者会被阻塞;当消费者从队列移除元素但队列为空时,消费者会被阻塞
双端的阻塞队列 BlockingDeque
阻塞队列又细分出来的概念TransferQueue
比BlockingQueue更进一步:
生产者会一直阻塞直到所添加到队列的元素被某一个消费者所消费(不只仅是添加到队列里就完事)。
新添加的transfer方法用来实现这种约束。
顾名思义,阻塞就是发生在元素从一个线程transfer到另外一个线程的过程当中,它有效地实现了元素在线程之间的传递
Map中:
ConcurrentMap 接口表明一个Map,它能够处理并发访问。
ConcurrentMap除了继承自java.util.Map的方法,还有一些本身的原子方法。
ConcurrentNavigableMap支持 NavigableMap 操做,且以递归方式支持其可导航子映射的 ConcurrentMap。
并发相关实现类 java.util.concurrent:
LinkedBlockingQueue
ArrayBlockingQueue
PriorityBlockingQueue
DelayQueue
SynchronousQueue
LinkedBlockingDeque
LinkedTransferQueue
CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentSkipListSet
ConcurrentHashMap
ConcurrentSkipListMap
ConcurrentLinkedDeque
ConcurrentLinkedQueue
Iterable:
实现这个接口容许对象成为 "foreach" 语句的目标。
实现了这个接口就代表已经听从"迭代定义的规则",拥有了迭代的能力.
他是一个顶级接口:
其中:
/** * Returns an iterator over elements of type {@code T}. * * @return an Iterator. */ Iterator<T> iterator();
Iterator:
public interface Iterator<E>
想要有迭代的能力,
须要实现Iterable接口
实现接口最重要的就是提供一个 iterator() 方法,用于返回迭代器
首先,集合自己并非迭代器,他只是有能够迭代的功能,因此是组合关系.
并且,若是继承的话,那么集合框架中:
Iterator接口的核心方法next()或者hasNext()
集合对象中就会包含当前迭代位置的数据(指针)
当集合在不一样方法间被传递时,因为当前迭代位置不可预置,那么next()方法的结果会变成不可预知
RandomAccess
标记接口
/ * * @since 1.4 */ public interface RandomAccess { }
用来代表其支持快速(一般是固定时间)随机访问。
主要目的是使算法可以在随机和顺序访问的list中表现的更加高效。
此接口的主要目的是容许通常的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。
好比:
Collections下的binarySearch方法的源码:
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) { if (c==null) return binarySearch((List<? extends Comparable<? super T>>) list, key); if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) return Collections.indexedBinarySearch(list, key, c); else return Collections.iteratorBinarySearch(list, key, c); }
从中能够清晰的看到,这个RandomAccess标记的做用
java集合框架中的全部具体类中都实现了Cloneable和Serializable接口
所以它们的实例都是可复制且可序列化的。
Serializable
public interface Serializable { }