集合框架指的是容器类。Java中大量持有对象的方式有数组和容器类两种方式。数组相较于容器类的优点在于:①随机访问效率高:由于是连续的存储空间,可以计算地址直接访问 ②类型确定:数组在创建时即可确定元素的具体类型 ③可存储基本数据类型。早期容器类默认存储Object类对象,这样无法在编译期对放入容器的元素类型进行检查,取出元素时也必须强制向下转型,而这不一定会成功。随着泛型的引入,容器类也可以声明确定的数据类型了。并且,由于自动包装机制,容器类也可以存储基本数据类型,而不是必须存储包装器类型的数据和引用类型数据。所以,数组的优势只剩下效率。如果效率高于灵活性,选择数组;否则,请选择容器类。所有的容器类尺寸都可以自动增长,但是牺牲了一定的效率。一般而言,优先使用容器类,以获取最大的灵活性。

容器类主要分为集合类和映射类:集合类存储的是单一的数据元素,比如一组对象。映射类存储的是相关联的键-值对,可以通过相应的方法获得键值对的键集合视图、值集合视图和键值对集合视图。对应这两个类,集合框架的顶层接口分别为Collection接口和Map接口。

集合类的继承层次为:

主要有三个继承的接口:Set:特点是没有重复元素;List:相当于线性表,保证了线性序列的特点;Queue:先进先出的队列。

映射类的继承层次为:

主要有HashMap、TreeMap和LinkedHashMap三个派生的接口;Hashtable一般由HashMap替代,不建议使用。映射类Map又称为关联数组或字典,它可以将一组对象映射到另一组对象中(不要求类型相同)。

以上实线表示继承(extends),虚线表示实现(implement)。集合类和映射类的继承层次加在一起,构建了完整的集合框架。

集合框架的设计理念提供一套“小而美”的API。API需要对程序员友好,增加新功能时能让程序员们快速上手。

为实现这个设计理念,需要实现以下几点:
一、保证最顶层的核心接口足够小:最顶层的接口(也就是Collection与Map接口)只需要实现一个集合应该具备的基本操作即可。最顶层的接口并不会区分该集合是否可变(mutability),是否可更改(modifiability),是否可改变大小(resizability)这些细微的差别。一些操作是可选的,在实现时抛出UnsupportedOperationException即可表示集合不支持该操作。集合的实现者必须在文档中声明那些操作是不支持的。

核心接口Collection接口的方法内容如下:

主要包括基本操作(计算元素个数、判断容器是否为空、判断是否包含某个元素、添加元素、移除元素)、迭代操作(获取迭代器对象的方法)、批量操作(判断是否包含一些元素、添加一些元素(并集)、移除一些元素(差集)、保留指定的一些元素(交集)、清空容器)和数组操作(将容器内容转换为数组存储形式)等。这些操作很显然是作为容器应当具备的常规操作,所以在最顶层接口中以抽象方法的形式给出(同时看出很多操作比数组灵活太多)。具体接口如下所示:

核心接口Map接口的方法内容如下:

常规操作与集合类相似,只是部分方法名称有所改变(get put等);同时由于存储的是键值对,所以提供了返回键集合、值集合、键值对集合的方法keySet()、values()和entrySet()。映射类相当于数学中的函数,y=f(x)。所以,键是不会重复的;对于每一个键都有唯一的值与之映射。因此键集合、键值对集合都是Set类型(没有重复元素),值集合是普通的集合类(一般用List存储)。

注意:Map接口中有一个内部类,接口Entry,该接口表示键值对对象。

二、根据需要扩展核心接口:在实际应用中对于具体的集合可能会有特别的操作需求,那么只需要扩展(extends)核心接口即可。

核心接口Collection主要的子接口有:

1、List接口:核心接口Collection接口中的数据元素是无序存储的。List接口直接继承了Collection接口,增加了有关元素位置的操作,允许有重复的元素。实现该接口的集合类是一个以列表形式存储数据的集合,可以对插入列表的每个元素的位置进行精确控制。所以该接口中增加了很多包含参数index的方法。List就是线性表,与数组一样是有顺序的线性容器。因为List类建立了元素与索引之间的关系,所以可以通过索引访问元素。

2、Set接口:直接继承于Collection接口,扩展的地方是要求元素不能重复。如果向实现Set接口的集合类中插入已经存在的元素,会插入失败。如果进一步要求对存储的元素进行排序,那么可以继续扩展Set接口,得到继承于Set接口的SortedSet接口。

3、Queue接口:直接继承于Collection接口。它与别的接口不一样的地方在于:Queue主要用于存储数据,而不是处理数据(A collection designed for holding elements prior to processing)。是一种在处理元素前用于保存元素的 collection。队列为Collection接口中的插入、提取和检查操作都提供了第二种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(nullfalse,具体取决于操作)。比如说Collection接口中插入操作为add(E e),而Queue额外提供了插入方法offer(E e)。队列通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素,不过优先级队列和 LIFO 队列(或堆栈)例外。对此也要求要扩展相应的方法。

核心接口Map的主要子接口有:

1、SortedMap接口:如果我不仅想要存储键值对,还希望集合中的键值对按照键进行排序,那么可以直接扩展Map接口,得到SortedMap接口。

三、实现接口的骨干方法:有了以上的接口层次,如果我想设计一个集合,直接实现这些接口即可。但是重写所有的抽象方法的代码量仍然是比较大的,JDK为了方便设计集合,已经提前实现了这些接口的主要操作,也就是对应的Abstract类。比如实现了Collection接口的主要方法的AbstractCollection抽象类,实现了Map接口的主要方法的AbstractMap抽象类,实现了List接口的AbstractList抽象类等等。如果想设计自定义的集合类,直接继承这些抽象类即可。

四、提供集合框架的具体实现类

集合类体系的具体实现类主要有:

List接口的具体实现:

1、ArrayList类:其实,就是顺序表。继承自AbstractList类,实现的是List接口。长度可以动态改变(就地扩容)的数组线性表类,因为是基于数组实现的,所以优点是:随机访问速度极快(O(1)),但是缺点是:不适合频繁的插入和删除操作(O(n))。特点:用默认的构造器初始化得到的ArrayList长度为10,就地扩容时容量变为:“原容量*3/2+1”。ArrayList是线程不安全的。请注意,尽管ArrayList底层使用数组实现该顺序表,所以具备了数组的随机访问速度快的优点,但是就效率而言还是比不过数组,这是因为:①空间利用率不高②扩容时移动大量的元素耗费时间。尽管如此,它在操作的灵活性上还是远超数组。

2、Vector类:ArrayList线程安全的版本。继承自AbstractList类,实现的是List接口。与ArrayList基本相同,区别就是:①扩容方案是变为原来的容量的两倍。②是线程安全的。所以一般在多线程中使用Vector代替ArrayList。

3、LinkedList类:继承自AbstractSequentialList类,实现的是List和Queue接口。是一个以链表形式实现的集合类。因为是基于双向循环链表实现,所以优点是:插入和删除操作效率高(O(1)),缺点是:随机访问速度慢,因为需要遍历链表(O(n))。

Set接口的具体实现:

1、HashSet类:继承自AbstractSet类,实现的是Set接口。受哈希表支持,基于hash算法来存储无重复的数据的类。其实HashSet底层是基于HashMap实现的。HashMap存储的是键值对,要求键不能重复,如果单独看键,将值设定为固定值,其实HashMap的键就是Set集合!HashSet访问速度最快,但是不保证集合的迭代顺序,特别是不保证集合的迭代顺序永久不变(因为可以会发生再哈希resize)。

2、LinkedHashSet类: 继承自HashSet类,实现的是Set接口。通过查看LinkedHashSet的源码可以发现,其底层是基于LinkedHashMap来实现的。对于LinkedHashSet而言,它和HashSet主要区别在于LinkedHashSet中存储的元素是在哈希算法的基础上增加了链式表的结构。速度比HashSet慢点,但是维持插入顺序。

3、TreeSet类:继承自AbstractSet类,实现的是SortedSet接口。TreeSet是一种排序二叉树。存入Set集合中的值,会按照值的大小进行相关的排序操作。底层算法是基于红黑树来实现的。TreeSet中的元素会按照相关的值进行排序,所以该方法比前两种都慢。

Queue接口的具体实现:1、PriorityQueue类:表示一个基于优先级堆的无界优先级队列。优先级队列是无界的,但是有一个内部容量,控制着用于存储队列元素的数组大小。它通常至少等于队列的大小。随着不断向优先级队列添加元素,其容量会自动增加。

2、各种Queue和栈的行为,都有LinkedList提供支持。

映射类体系的具体实现类主要有:

1、HashMap类:继承AbstractMap类,实现了Map接口。HashMap基于hash表实现,若key的hash值相同则使用链表方式进行保存(拉链法),即链表的数组。新建一个HashMap时,默认的话会初始化一个大小为16,负载因子为0.75的空的HashMap。HashMap至多允许一个key为null,允许多个value为null。

2、LinkedHashMap类:继承自HashMap,实现了Map接口。同样也是基于hash算法实现,但是LinkedHashMap与HashMap的不同之处在于,LinkedHashMap并不是一个”链表的数组“,而是一个双重链表。该类线程不安全,如果你想在多线程中使用,那么需要使用Collections.synchronizedMap方法进行外部同步。

3、HashTable类:继承于Dictionary类,实现了Map接口。此类实现一个哈希表,该哈希表将键映射到相应的值。HashTable不允许任何null元素,它是线程安全的。该类一般被废弃,由HashMap替代。

4、TreeMap类:继承AbstractMap类,实现了SortedMap接口。基于红黑树实现了键值对的排序。该类线程不安全,如果在多线程中使用,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。

补充:1、Collection接口中有将集合类转化为数组的方法toArray(),所以任何集合类都可以通过该方法实现集合和数组的交互。

2、Map接口有获取键集合视图的方法keySet(),有获取值集合视图的方法values(),有获取键值对集合视图的方法entrySet(),通过这三个方法可以实现映射类和集合类的交互。

总结:集合框架就是由一系列接口、接口的骨干实现抽象类以及具体实现类组成的模板。最顶层的接口包含核心方法,根据实际需求衍生了子接口以及子接口的子接口。为方便实现接口,对这些接口进行了初步实现,得到了骨干实现抽象类。最后封装好了完善的具体实现类,用作不同的存储和操作需求。

集合框架分为两个大枝,集合类体系和映射类体系,二者可以(转化)交互。集合类也可以与数组(转化)交互。

单一的元素请选择Collection类:

①List类是有顺序的线性表,允许元素重复:如果频繁随机访问,采用ArrayList实现;频繁在容器内部进行插入、删除操作,请选择LinkedList。

②Set类不允许重复的元素:为了查找速度,不在乎元素存储顺序,请选择HashSet;要求元素顺序与插入顺序一致,选择LinkedHashSet,但是比前者慢;为了得到一个按值有序的容器,可以使用TreeSet,最慢。

③Queue类和Stack都可以由LinkedList实现。

键值对的存储请选择Map类:

为了速度请选择HashMap;为了保证有序请选择TreeMap;为了保证与插入顺序一致,请选择LinkedHashMap(与Set一样)。

在具体使用某个容器类时,对象创建一定是使用具体的实现类,但是引用有两种选择:

①List<Type> list=new ArrayList<>(); 

这种方式选择上层接口作为引用(向上转型),因为具体实现类对父类接口的实现(其实就是继承),需要实现其所有的抽象方法(其实就是重写方法),而这种重写是覆盖,所以通过list上层接口引用不会影响调用具体实现类中的方法。这种方法还有个优点是灵活,如果需要切换另一种LIst实现类直接向下转型即可。

②ArrayList<Type> list=new ArrayList<>();

作为最常见的对象创建方式,这种方式优点是:可能某些具体实现类不仅实现了父类接口的全部抽象方法(即重写),还增加了在该情景下对数据独特的操作方法。这时候如果向上转型,显然会丢失这部分方法;所以直接将引用类型确定为具体实现类本身即可。

另外,所有的Collection接口实现类都提供带有Collection<T>参数的构造器,这是为了方便实现容器类之间的转换。使用该方式还有一些额外的效果,比如:将List容器转存至Set容器,自动消除了重复元素,如果是TreeSet还能按值排序;将List容器转存至Map容器时,可以将每个元素出现的次数作为值,元素作为键,这样可以统计频度,等等。

数组和容器都可以使用for-each方式进行迭代遍历,但是该方式只能读。容器类提供了iterator()方法获取一个轻量级对象——Iterator迭代器对象。迭代器对象进行容器类的遍历时需要注意:①单向移动,向后遍历 ②可以移除元素,但是每次只能移除一个元素,重复调用remove()方法会空指针异常。其实移除元素,移除的是迭代器游标位置的元素,初始时游标指向第一个元素之前。

另外,List类还额外提供了listIterator()方法来获取ListIterator,这种迭代器威力更加强大,可以自由的双向移动,还可以设置初始化游标的位置。不仅可以移除元素,还可以插入、修改元素。

Java集合框架概述的更多相关文章

  1. Java集合框架概述和集合的遍历

    第三阶段 JAVA常见对象的学习 集合框架概述和集合的遍历 (一) 集合框架的概述 (1) 集合的由来 如果一个程序只包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序. 通常,程序 ...

  2. Java集合源代码剖析(一)【集合框架概述、ArrayList、LinkedList、Vector】

    Java集合框架概述 Java集合工具包位于Java.util包下.包括了非常多经常使用的数据结构,如数组.链表.栈.队列.集合.哈希表等.学习Java集合框架下大致能够分为例如以下五个部分:List ...

  3. (转)Java集合框架:HashMap

    来源:朱小厮 链接:http://blog.csdn.net/u013256816/article/details/50912762 Java集合框架概述 Java集合框架无论是在工作.学习.面试中都 ...

  4. Java集合框架:HashMap

    转载: Java集合框架:HashMap Java集合框架概述   Java集合框架无论是在工作.学习.面试中都会经常涉及到,相信各位也并不陌生,其强大也不用多说,博主最近翻阅java集合框架的源码以 ...

  5. Java集合框架详解(全)

    一.Java集合框架概述 集合可以看作是一种容器,用来存储对象信息.所有集合类都位于java.util包下,但支持多线程的集合类位于java.util.concurrent包下. 数组与集合的区别如下 ...

  6. java 集合框架(一)概述

    一.概述 Java Collection Framework (JCF) 提供给我们一系列的类和接口,方便开发者处理集合对象. 在Java 2之前,Java是没有完整的集合框架的.它只有一些简单的可以 ...

  7. Java 集合系列之一:JCF集合框架概述

    容器,就是可以容纳其他Java对象的对象.Java Collections Framework(JCF)为Java开发者提供了通用的容器 java集合主要划分为四个部分: Collection(Lis ...

  8. 概述Java集合框架

    JAVA集合框架主要分为三个部分:接口,实现和算法.接口是指以Collection和Map为起始的一系列公用接口,其中还有Vector接口,也就是迭代器,Collection接口下面又有List 和S ...

  9. 【JAVA集合框架之工具类】

    一.概述 JAVA集合框架中有两个很重要的工具类,一个是Collections,另一个是Arrays.分别封装了对集合的操作方法和对数组的操作方法,这些操作方法使得程序员的开发更加高效. public ...

随机推荐

  1. 动态横向(水平)合并GridView数据行DataRow的列

    前一段时间,Insus.NET有写过<动态合并GridView数据行DataRow的列>http://www.cnblogs.com/insus/p/3238348.html, 那是纵向( ...

  2. NSNull空值

    1.前言 作为占据空间的一个空值,如用在数组或字典中占据一个没有任何值的空间. 1.1 NULL & nil 的区别: nil 是 OC 的,空对象,地址指向空的对象,指针地址指向的是 NUL ...

  3. Ubuntu小记

    一. Ubuntu分区记忆 参考教程调整: 1. /boot用于安装grub,设为主分区 2. /根目录20G一般足够 3. /home剩下的给home 4. swap空间=物理内存 挂载点 大小 类 ...

  4. 微信小程序小结(4) -- 分包加载及小程序间跳转

    分包加载 某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载(主要是空间不够用,哈哈~). 在构建小程序分包项目时,构建会输出一个或多个功能的分包,其中 ...

  5. 区块链中的密码学(二)-RSA算法分析和实现

    密码学领域中,加密算法主要分为对称加密和非对称加密,随着信息时代安全性要求越来越高,对称加密因为其易被破解的原因逐渐被舍弃.而RSA算法是目前密码学世界中比较流行的非对称加密算法,命名是根据其发明者R ...

  6. Python之函数练习题

    一.简述普通参数.指定参数.默认参数.动态参数的区别 普通参数:就是放入一个形参,当放入实参时,需要按照顺序给形参值. 指定参数:放入实参时是指定的,不用按照顺序给形参,都能让形参获得相应的参数. 默 ...

  7. [linux]阿里云主机的免密码登陆安全SSH配置与思考

    公司服务器使用的第三方云端服务,即阿里云,而本地需要经常去登录到服务器做相应的配置工作,鉴于此,每次登录都要使用密码是比较烦躁的,本着极速思想,我们需要配置我们的免登陆. 一 理论概述 SSH介绍 S ...

  8. github 的使用步骤

    1. github是一个git项目托管网站 注册地址:https://github.com/signup/free 2. 安装git程序,执行下面操作 $ cd ~/.ssh //检查计算机ssh密钥 ...

  9. POJ3050 -- Hopscotch 简单的dfs搜索

    原题链接:http://poj.org/problem?id=3050 (一些文字过会儿再说现在有事儿) #include <cstdio> #include <set> us ...

  10. 【NOIP 2009】最优贸易

    描述 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通 ...