概述

JDK1.2时候,Java的设计者们对Java存储数据的容器进行了大刀阔斧的改革。推出了庞大的Java集合框架体系。

Java集合框架拥有非常庞大的容器体系,通过不同的实现对不同场景的元素存取达到高性能。

Java集合拥有2大常用分支,一支是Collection体系的存储单个元素的集合,另一分支存储着Key——Value健值对形式的Map体系。

Collection体系中,又拥有3大常用分支分为List,Queue,Set。

而Map体系中,分为无序Key-Value和有序Key-Value形式2大类。

集合的子类非常多,本文将介绍常用的集合实现类。

下图展示了集合的继承关系,通过掌握继承关系图,可以更加容易学习集合框架。

Java常用集合类架构图

java 集合机构图 java集合体系结构图_双向链表

Java Map 集合类架构图

java 集合机构图 java集合体系结构图_Java_02

Collection 接口

整个Collection接口是集合的根接口,Collection用来描述一组对象,描述集合这个概念

同时Collection也提供了基本操作集合的方法

一些 Collection 允许有重复的元素,而另一些则不允许。一些 Collection 是有序的,而另一些则是无序的。

Collection子接口分为常用的三大分支:List(有序集合)Queue(队列)Set(无序不可重复)

Collection 规范了集合的常用方法,包含添加,删除,是否包含,迭代器等常用功能。

java 集合机构图 java集合体系结构图_java2的集合框架_03

List 接口

List接口用来描述一组有序集合,集合记录元素插入顺序。

List接口允许用户通过索引对元素精准控制,包括插入,修改,访问元素。

List接口通常允许元素重复。

List 接口在Collection接口基础上,提供了更多通过索引操作元素

java 集合机构图 java集合体系结构图_List_04

ArrayList

List 接口的可变数组的实现类,其拥有父接口List有序集合,索引控制,重复元素的等特点。

允许插入null所在内的元素。

此类还提供一些方法来操作内部用来存储列表的数组的大小。

在添加元素前,ArrayList使用 ensureCapacity 操作来增加容量。

// 1. 创建ArrayList对象
ArrayList list = new ArrayList<>();
//2. 添加设置元素的值
list.add("hello");
list.add(1, "world");
list.add("java");
//3. 删除元素
list.remove("world");
list.remove(0);
//4. 修改元素的值
list.set(0,"Hello");
//5. 查询相关
list.get(0);
list.forEach((String e) -> {
System.out.println("e = " + e);
});

Vector

Vector 是JDK1.0提供的集合类。

Vector 的底层实现与ArrayList相同,并提供了ArrayList相同的功能。

Vector 所有方法都通过增加synchronized来实现线程安全。

Vector 是线程安全的,但同时效率相比ArrayList较低,在JDK1.2中,ArrayList是Vector的替代方法,推荐使用ArrayList。

LinkedList

List接口的双向链表实现。

其拥有父接口List有序集合,索引控制,重复元素的等特点。

LinkedList 在操作列表,头元素,尾元素 (get、remove、insert)时,提供了统一的命名方式,所以LinekedList 这些操作允许将链表当作栈、队列或双端队列使用。

LinekedList所有操作都是按照双向链表方式执行的,当按索引获取/修改/插入元素时,将从开头或结尾遍历链表(从靠近指定索引的一端)。

LinkedList 由于双向链表的特性,以及实现了Queue,和Dequeue接口,将而外提供栈,和双端队列的特点。

java 集合机构图 java集合体系结构图_Java_05

6. 作为队列使用的LinkedList

LinkedList queue = new LinkedList<>();
// 向队列头插入元素
queue.offer(" world");
// 默认 linked last
queue.offer("hello");
queue.offerFirst("java");
//queue = [java, world, hello]
System.out.println("queue = " + queue);
// 获取 队列 头、尾元素,但不移除
String v = queue.peek();
v = queue.peekFirst();
v = queue.peekLast();
// 获取 队列 头、尾元素,并且移除出队列
v = queue.poll();
v = queue.pollLast();


作为栈使用的LinkedList

LinkedList stack = new LinkedList<>();
stack.push("hello");
stack.push("world");
stack.pop();
stack.pop();

Queue & Deque

Queue队列维护着一个先进先出(First In First Out)数据结构

Queue接口与List、Set同一级别,都是继承了Collection接口。

Deque一个线性 Collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写。

此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。

LinkedList使用双向链表实现了基本的队列操作

java 集合机构图 java集合体系结构图_java 集合机构图_06

Set

Set集合在Collection集合基础上 提供 不包含重复元素的功能。

Set不包含满足 e1.equals(e2)或(el==e2) 的元素,并且最多包含一个 null 元素。

Set集合一般不维护着元素的插入顺序。

同时Set集合不提供对索引精准控制。

Set实现类底层采用与之名称对应Map实现类实现,通过将元素存储在Map的Key中,Value采用全局静态Object的实例。

Set接口提供着和Collection相同的方法列表规定。

java 集合机构图 java集合体系结构图_java2的集合框架_03

HashSet

Set集合基于哈希表实现类---对应HashMap。

HashSet内部使用HashMap的key来存储元素,value使用静态的Object对象实例。

HashSet提供了Set接口的所有方法的实现,在判断是否包含某元素和删除插入元素上,拥有卓越的性能。

LinkedHashSet

LinkedHashSet 可预知迭代顺序Set接口的实现类,基于哈希表+双向链表实现---对应LinkedHashMap。

LinkedHashSet 继承自 HashSet。

通过调用父类接收3个参数构造方法,在内部初始化LinkedHashMap存储元素,所以其实现代码写在父类里,但父类HashMap此初始化方法是 默认 修饰符,只有包访问权限,所以只能子类初始化。

与 HashSet 的不同之外在于,后者在哈希表的基础上,增加了一个维护所有条目的双向链表,此链表定义了迭代顺序,即插入元素顺序。

TreeSet

TreeSet 可排序的Set接口的实现类---对应TreeMap。

TreeSet 内部使用了TreeMap(红黑树)实现排序功能,同样将元素作为TreeMap的Key。

TreeSet 默认使用元素的自然排序,元素需要实现Comparable接口。

TreeSet 同时允许,通过传递比较器(Comparator)实现类,自定义排序。

// TreeSet 自然排序
TreeSet set = new TreeSet<>();
set.add(10);
set.add(7);
set.add(2);
set.add(9);
// set = [2, 7, 9, 10]
System.out.println("set = " + set);
// 实现 Comparator 接口,自定义排序
TreeSet set = new TreeSet<>(new Comparator() {
@Override
public int compare(Student o1, Student o2) {
int num = o1.getAge() - o2.getAge();
num = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
return num;
}
});


Map

Map接口是Map体系的根接口。

Map特点是将键映射到值的对象,每个键最多只能映射到一个值

Map中同样相同的健只能存储一个。

java 集合机构图 java集合体系结构图_java 集合机构图_08

HashMap

HashMap是Map接口基于哈希表的实现类。

其拥有Map接口的所有实现方法。

HashMap不保证映射的顺序,即插入元素的顺序。

关于哈希表的实现,请参考。

LinkedHashMap

LinkedHashMap是Map接口 哈希表和链表的实现。

其拥有Map接口所有的方法,并具有可预知的迭代顺序。

后者维护着一个运行于所有条目的双重链表。此链表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序。

Hashtable

Hashtable 与 HashMap同样是基于哈希表实现类。

Hashtable 是JDK1.0推出的键值映射集合,在JDK1.2后此类就被改进以实现 Map 接口。

Hashtable不保证映射的顺序,即插入元素的顺序。

与HashMap不同的是,Hashtable所有方法都是线程同步的。

Properties

Properties 类是Hashtable的子类,其同样是键值映射集合。

不同的是 Properties类表示了一个持久的属性集。

Properties 可以配合IO流,从流中读取或将数据写入流中。

Properties 中每个键及其对应值都是一个字符串。

使用 Properties 时,不建议使用 put 和 putAll 方法,因为他们允许插入String之外的值。

Java开发中,常常将一些配置信息存放在.properties文件中,而使用Properties类的load方法进行加载。

java 集合机构图 java集合体系结构图_java2的集合框架_09

7. 使用Properties存储键值对,并存储到文件中。

Properties p = new Properties();
p.setProperty("name","李洛克");
p.setProperty("age","18");
// 将 properties 保存到 classPath 路径下
String classPath = ClassLoader.getSystemResource("").getPath();
OutputStream os = new FileOutputStream(classPath + "test.properties");
p.store(os,"注释");
// 所谓classpath 就是指使用 javac -d classpath 的路径
// 在idea中就是 project compiler output path
// maven 工程会自动将 标记为 resources 文件夹下文件拷贝到classpath路径下


读取文件中properties的内容

// 从 classPath 下加载 properties文件
InputStream is = ClassLoader.getSystemResourceAsStream("test.properties");
Properties p = new Properties();
p.load(is);
System.out.println(p.getProperty("name"));
System.out.println(p.getProperty("age"));


TreeMap

可自然排序和自定义规则排序的Map集合。

TreeMap 是基于红黑树(Red-Black tree)的 NavigableMap 接口的实现。

使用TreeMap实现排序方法

// 自定义元素排序,按照key的长度 进行排序
Map map = new TreeMap<>(new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
map.put("a",1);
map.put("bbbb",1);
map.put("ccc",1);
// map = {a=1, ccc=1, bbbb=1}
System.out.println("map = " + map);