概述
集合是一组复合元素的容器,用来存储,检索,控制聚合数据并提供它们之间的通信方法。
java的集合框架提供了表示和操控集合的统一架构。所有的集合框架都包含下面三个方面:
接口:即集合的抽象数据结构。接口允许我们独立地操纵集合而不用考虑集合的具体实现
实现:即接口的具体实现类。从本质上来讲,它们是可重用的数据结构
算法:即在实现了集合接口的对象上执行有用的计算,比如排序和搜索,的方法。算法是多态的:同名的方法可以被任何合适的接口实现类调用,从本质上来讲,算法是可重用的功能
核心集合接口封装了不同类型的集合,它们是java集合框架的基础,形成了下图所示的层级结构
从上图中可以看到,Set是一种特殊的Collection,而SortedSet是一种特殊的Set……诸如此类。
需要注意,上图中有两个不同的树,Map并不是Collection的子接口,而是一个独立的接口
所有的集合接口都是泛化的。比如下面是Collection接口的声明:
public interface Collection<E>...
“<E>”表示该接口是通用的。当我们声明一个Collection接口时,最好指定该接口包含的对象类型,以便让编译器在编译时检验输入的对象是否正确,从而减少运行时抛出的错误。
对于接口中的方法,有很多是可选择实现的,就是说,它的实现类可以实现该方法,也可以不实现,根据具体需要来决定
我们先来看一下核心的集合接口,对它们有一个整体的感性认识:
Collection接口:集合框架的根接口。它是集合类框架中最具一般性的顶层接口。Java平台没有提供任何该接口的直接具体实现类,但是提供了具有各种不同特性的子接口
Set接口:不允许包含重复值的集合
List接口:可索引的集合,可以包含重复值。使用该接口时我们通过索引对元素进行精准的插入和查找
Queue接口:该集合适用于组织一个队列,队列中的元素按照优先级进行处理。除了继承自Collection接口的方法,该接口还提供了另外的插入、提取和检验方法。典型的队列是符合“先进先出”(FIFO:First In,First Out)原则的,优先级队列是一种例外,它按照元素的优先级顺序排列元素。无论按照什么原则排序,队头元素总是首先被检出。每个Queue接口的实现类必须指定它的排序原则
Deque接口:与Queue的不同之处在于它是一个双端队列,在两端都能插入和移除元素,它继承并扩展了Queue接口
Map接口:提供了键值对(key/value)的映射关系的集合。关键字不能有重复值,每个关键字至多可映射一个值
SortedSet接口:以升序的原则维持着集合中的元素顺序。
SortedMap接口:以关键字升序的原则维持着集合中的元素顺序
以上接口的通用实现类(这里的通用实现类一般是指适用于单线程环境下的实现类,在JDK中有针对多线程并发环境下的特殊实现类)总结如下
接口
哈希表实现
可变数组实现
树实现
链表实现
哈希表+链表实现
堆实现
Set
HashSet
TreeSet
LinkedHashSet
List
ArrayList
LinkedList
Queue
LinkedList
PriorityQueue
Deque
LinkedList
Map
HashMap
TreeMap
LinkedHashMap
可以发现,LinkedList同时实现了List、Queue、Deque三个接口。
SortedSet接口和SortedMap接口没有在上表中列出,在上面的层次结构图中可以看到,它们分别是Set和Map的子接口,TreeSet和TreeMap就是它们的实现类
以上提到的所有通用实现类都支持为null的元素(或者键/值),不过有的只能包含一个null,有的可以包含多个null;所有通用实现类都实现了Serializable,是可序列化的对象;所有通用实现类都提供了一个clone方法用于复制对象;所有通用实现类都是线程不安全的(即是不同步的);所有通用实现类都提供了”fail-fast”机制的迭代器
关于“fail-fast”机制,来看一个实例:
Map<String,String> map = new HashMap<String,String>(); map.put("first", "Jay"); map.put("second","Jack"); map.put("third", "Jim"); Iterator<String> it= map.keySet().iterator(); while(it.hasNext()){ System.out.println(map.get(it.next())); if(map.containsKey("second")){ map.remove("second"); } }
以上代码试图使用Iterator迭代Map里的键值,如果键值为“second”则删除Map中的该元素。运行一下可以发现,执行删除操作时会报java.util.ConcurrentModificationException异常,即便这是单线程。
Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
但是,如果上面的代码进行的不是删除操作,而是覆盖掉原来的元素:
if(map.containsKey("second")){ map.put ("second","Joe"); }
就不会报错,上面的程序中的Iterator是对Map的Key进行迭代,虽然对Map进行了修改,但是只改变了一个元素的value,整个Map的KeySet并没发生变化
如果想要成功的执行删除操作,就需要先对Iterator进行对应的删除操作
Map<String,String> map = new HashMap<String,String>(); map.put("first","Jay"); map.put("second","Jack"); map.put("third","Jim"); Iterator<String> it= map.keySet().iterator(); while(it.hasNext()){ System.out.println(map.get(it.next())); if(map.containsKey("second")){ it.remove(); //先对Iterator进行删除 map.remove("second"); } }
除了上面提到的实现类之外,还有两种不常用的实现类: Vector和 Hashtable, 它们是线程安全的。这是低版本JDK的遗留产物,对于集合而言,大多数情况都没必要支持线程安全。在需要考虑多线程操作的环境下,JDK也提供了多种方法来将上面的通用实现类转换成线程安全的类如果多个线程同时访问一个实现类,而其中至少一个线程修改了该类,那么它必须保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用Collections.synchronizedMap方法来“包装”map。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
Map map = Collections.synchronizedMap(new HashMap(...));
最后
以上就是隐形学姐为你收集整理的集合框架之集合类概述的全部内容,希望文章能够帮你解决集合框架之集合类概述所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复