跳至主要內容

Java 集合框架体系

Zenghr2021年4月25日大约 14 分钟Java

Java 集合框架体系

提示

本文用于整合 集合框架的知识体系,如有不正确的地方请指出,感谢。

JDK Version:1.8.0.212

数据结构概述

数据结构就是数据在计算机中存储的方式,不同的数据结构,底层采用不同的存储算法,在执行具体的操作时,不同的算法会有不同的效率,有的查询效率慢,有的删除添加效率快等

常见的数据结构

在日常的开发中,往往根据业务的需要,选择不同的数据结构,选择合适的集合类

这么多数据结构,我们又可以根据其数据结构特性(是否连成一条线),分为 线性数据结构非线性数据结构

线性数据结构

数组(Array)

把具有相同类型的数据有序地组织在一起,称为数组。数组使用 索引/下标 来表示元素的存储位置。

性能分析

总结:基于数组(Array)的数据结构做查询修改是非常快的,添加和删除就比较慢

链表(Linked)

每个元素在内存中的位置不是有序的,而是通过在每个元素中记录下一个元素的位置链接在一起,称为链表。

链表又分为 单向链表 和 双向链表

mark
mark
mark
mark

性能分析

总结:链表(Linked) 数据结构对于 添加删除操作较快,查询修改比较慢

队列(Queue)

队列是一种特殊的线性表,它只允许在头部进行删除操作,尾部进行添加操作,是一种受限制的线性表。

队列又分为 单向队列 和 双向队列:

单向队列
单向队列
双向队列
双向队列

总结:擅长操作头部和尾部

栈(stack)

栈(stack)又名堆栈,它是一种运算受限的线性表,后进先出(LIFO)。

栈结构

栈结构仅允许在表的一端进行插入和删除运算,这一端被称为 栈顶,相对地,把另一端称为 栈底
向一个栈中插入新元素又称作 入栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素。从一
个栈中删除元素又称作 出栈

集合框架体系

集合是Java中提供的一种容器,可以用来存储多个数据,根据不同的存储方式形成的体系结构,就叫做集合框架体系。

集合继承关系
集合继承关系

每一种容器类底层拥有不同的底层算法(数据结构)。

Iterable 接口

从上图可以看出 Iterable 接口是所有集合的超类, 实现此接口允许对象成为 for-each 循环的目标,它是 Java 中的一种 语法糖

List<Object> list = new ArrayList();
for (Object obj: list){}

其他遍历方式

Iterable 接口有一个方法,就是

Iterator<T> iterator();

该方法能创建一个轻量级的迭代器,用于安全的遍历元素,移除元素,如果在for-each中删除集合中的元素,是不安全的,会抛出 ConcurrentModificationException 异常,所以尽量使用迭代器的方式进行遍历,删除元素

for(Iterator it = coll.iterator(); it.hasNext(); ) {
	System.out.println(it.next());
}

提示

集合容器主要包括 Collection 和 Map 两种,Collection 存储对象的集合,而 Map 存储 键值对 (key - value)

Collection 顶层接口

Collection 是顶层接口,它主要用于定义集合的约定

List 接口继承于 Collection 接口,同时也是 ArrayList、LinkedList 的父类

Set 接口跟 List 接口处于同级的层次,也继承于 Collection 接口,同时也是 HashSet、TreeSet 的父类

Map 是一个支持 key-value 存储的对象,Map 不能包含重复的 key,注意 Map 不继承于 Collection 接口

提示

实现类命名方式:底层数据结构 + 实现的集合接口。e.g:ArrayList (数组结构 + List 接口)

List 接口

List接口是 Collection接口 的子接口,List 接口定义了一种规范,要求该容器允许记录元素的添加顺
序,也允许元素重复。那么List接口的实现类都会遵循这一种规范

常用实现类

常用 API

添加操作

删除操作

修改操作

查询操作

ArrayList

ArrayList 是实现了 List 接口的 可扩容数组,它的内部是基于数组实现的,不是线程安全

transient Object[] elementData;

Vector

Vector 同 ArrayList 一样,都是基于数组实现的,只不过 Vector 是线程安全的容器。内部每个方法使用 synchronized 进行同步,避免多线程引起的安全问题,但是也导致了 Vector 效率低下

LinkedList

LinkedList 类,底层采用链表算法,实现了链表,队列,栈的数据结构。无论是链表还是队列主要
操作的都是头和尾的元素,因此在 LinkedList 类中除了 List 接口的方法,还有很多操作头尾的方法

注:这个实现也不是线程安全的

常用 API

栈操作

单向队列

双向队列

提示

LinkedList之所以有这么多方法,是因为自身实现了多种数据结构,而不同的数据结构的操作方法
名称不同

Stack

Stack 表示栈,是 Vector 类的子类,具有先进后出的特性,拥有push(入栈),pop(出栈)方法

Set 接口

Set 是 Collection 子接口,Set 接口定义了一种规范,也就是该容器不记录元素的添加顺序,也不允
许元素重复,那么 Set 接口的实现类都遵循这一种规范。

常用实现类

常用 API

HashSet

HashSet 是 Set 接口的实现类,底层采用哈希表实现,不能保证元素的有序,保证了元素的唯一,运行存储 NULL 元素,注意 它不是线程安全的容器。

查看源码可知,其内部是 HashMap 的实例

private transient HashMap<E,Object> map;
public HashSet() {
    map = new HashMap<>();
}

底层工作原理

内部使用哈希表实现,每个元素需要先计算 哈希值,再通过 散列函数 计算出元素的位置

如果该位置存在元素,则去除该位置上 链表 的所有元素,通过 equals 方法依次比较,如果有重复的元素,则不添加,没有重复的元素,则在其 链表 后面添加,如下图👇

mark
mark

hashCode 和 equals

根据其工作原理,我们可以得知,在哈希表中元素的 hashCode 和 equals 方法很重要

每一个存储到好像表中的对象,都得覆盖hashCode和equals方法用来判断是否是同一个对象,一
般的,根据对象的字段数据比较来判断,通常情况下equals为true的时候hashCode也应该相等

TreeSet

TreeSet 是 Set 接口的实现类,底层采用红黑树算法实现,会对存储的元素进行排序,默认是自然排序(从小到大)

注意:TreeSet 不允许存储 NULL 值

mark
mark

当我们使用 TreeSet 存储元素时,此时要求元素必须具备 比较大小 的能力,也就是实现 java.util.Comparable 接口

Comparable 接口

TreeSet 会调用元素的 compareTo 方法来比较元素的大小关系,然后将集合元素按照升序排列

public interface Comparable<T> {
	public int compareTo(T o);
}

比较规则,拿当前元素和另一个元素比较:

如果我们存储进 TreeSet 集合中的元素没有 比较功能,可以让该元素实现 Comparable 接口,并
覆盖compareTo方法,在该方法编写比较规则,否则添加会报错。

class ComparableExample implements java.lang.Comparable<User> {
    public int compareTo(ComparableExample o) {
        // ... 比较规则
        return int;
    }
}

Comparator 接口

当原有的排序规则不满足我们的需求时,我们可以提供一个 外部比较器(Comparator) 通过 TreeSet 构造器传入该比较器的实现类

public interface Comparator<T> {
  int compare(T o1, T o2);
}

使用方法:

因为 Comparator 实现类只是用一次,我们可以使用匿名内部类,JDK8 提供了 lambda 表达式,我们可以简写

// 需求:通过比较字符串长度
// 原有的 String 自带的比较规则不满足需求,使用 Comparator
public static void main(String[] args) {
    TreeSet<String> treeSet = new TreeSet<>(Comparator.comparingInt(String::length));
    treeSet.add("abc");
    treeSet.add("apple");
    treeSet.add("zeng");

    System.out.println(treeSet);
}

Map 接口

Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value

Map 结构如下👇

mark

其实能看出一个 Map 其实就由多个 key-value(键值对)组成的,每一个键值对我们使用Entry表示,键(key值)不可重复,value值可以重复

所以也可以说 Map 的 key 是存储在 set 集合中,不可重复

常用实现类

常用 API

HashMap

HashMap 底层基于哈希表算法,Map中存储的 key 对象的 hashCode 值决定了在哈希表中的存储位
置,因为Map中的 key 是 Set,所以不能保证添加的先后顺序,也不允许重复

因为 HashSet 内部使用的就是 HashMap 所以两两者原理一致,HashSet 只是在 HashMap 上封装了一层,其底层工作原理一致

TreeMap

TreeMap底层基于红黑树算法,因为Map中的 key 是 Set,所以不能保证添加的先后顺序,也不允
许重复,但是Map中存储的 key 会默认使用自然排序(从小到大),和 TreeSet 一样,除了可以使用自然
排序也可以自定义排序

底层工作原理和 TreeSet 一致

集合框架 工具类

Arrays

Arrays类是数组的工具类,其中包含对数组的拷贝、格式化、排序查询等方法

Collections

Collections 是一个操作 Collection(Set、 List )和 Map 等集合的工具类

常用 API

排序操作

查找和替换

解决多线程并发访问集合时的线程安全问题

Collections提供了基本每种集合类型都有的方法: synchronizedXxx() 方法,以解决多个线程同时操作一个集合的并发问题

//通过如下的方法保证List的线程安全性
List list = Collections.synchronizedList(list1);