第一章 集合框架
1.1集合(Collection)体系结构
Collection接口:它是Java集合体系结构的根接口。
描述的是元素独立的集合。有的集合可以允许元素重复。有的集合是有序的(前后顺序)
子接口:
List接口:元素有顺序。
ArrayList类
LinkedList类
Vector类
Set接口:元素不能重复。
HashSet类
LinkedHashSet类
TreeSet类
Queue接口:队列。
DeQue接口:双端队列。
Map接口:是一种哈希的实现。
每一个元素,都由两部分组成:键(key)-值(value),称为键值对(key-value);
HashMap类
LinkedHashMap类
HashTable类
TreeMap类
ConcurrentMap类
请介绍一个Java中的集合?
1.2 List接口
1.2.1特点
有序,所以可以使用下标访问
通常允许重复元素
有的可以使用null,有的不可以
1.2.2常用方法
增
add
addAll
删
clear
remove
removAll
改
set(index,Object)
查
contains
get(index)
size()
isEmpty()
indexOf();
lastIndexOf()
subList()
1.2.3ArrayList类
这是一个可变数组。长度是可以变化的。
底层实现是数组。
特点:
- 通过下标访问时,速度很快!可以通过起始地址+下标偏移量直接计算出元素的位置,时间复杂度O(1)
- 在中间位置插入和删除元素时,需要将指定位置后的元素进行移动。这时的效率很低,时间复杂度O(n)
- 在尾部插入和删除元素,效率也很高。
什么情况下使用ArrayList???
查询操作多,从中间插入或删除的操作少。
默认初始容量是10
扩容时,扩充为原来容量的1.5倍,如果还不够用需要多少扩充多少
1.2.4LinkedList类
长度也是可以变化的。
底层是一个链表(双向链表)实现的。
什么是链表?
链表示一种数据结构(计算机中对数据进行组织和存储的结构)
根据数据之间的关系进行分类
- 元素之间没有任何关系,只存在于一个集合中。
- 一对一:一个元素最多只有一个前驱元素,最多只有一个后继元素。
一对一关系称为线性结构
数组:内存连续,大小固定的。
链表:内存不连续。元素包含两部分:值+指针(指向下一个元素)
单链表:只有头,没有尾
双端链表:有头也有尾,尾部追加也很方便
循环链表:尾部元素的指针直接指向第一个元素
双向链表:两套指针,既可以从前往后找,也可以从好后往前找
双向循环链表:既是双向的,也算是循环的
队列:只能在两端进行操作。从一头添加(入队),从另一头取(出队)。先进先出,后进后出
栈:下只能从一端进行操作,先进后出,后进先出。
- 一对多:一个元素最多只有一个前驱元素,但可以有多个后继元素。
称为树。
二叉树(满二叉树、完全二叉树、二叉排序树BST、平衡二叉树、平衡二叉排序树AVL、红黑树),B树,B+树,B*树
- 多对多:一个元素可以有多个前驱元素,也可以有多个后继元素。
称为图。
有向图和无向图。广度优先遍历、深度优先遍历。
因为使用链表实现的,所以
- 根据下标志查找元素的操作效率很低,需要从头一个个遍历元素,O(n)
- 如果在中间位置插入或删除元素,效率很高只需要重新设置前后元素的指针即可。
1.2.5Vector类
与ArrayList基本一致。是线程安全的
一般不用。
1.2.6 List集合的遍历
1.循环生成下标
2.增强for循环 foreach
foreach基于迭代器实现
数组在foreach时实现的是for循环
3.Iterator——迭代器
当一个集合元素在进行迭代过程中,不能对原始集合进行添加或删除元素的操作。
可以替换某个元素
迭代器自己可以操作元素,但只能删除当前读到的元素。
总之就是不能改变元素数量!!!(除了iterator.remove)
1.3 set接口
1.3.1特点
不包含重复元素。
有些可以包含null,有些不能
没有前后顺序
1.3.2常用方法
增
add
addAll
删
remove
clear
查
contains
isEmpty
size
1.3.3 HashSet类
不保证迭代顺序
允许null
线程不安全
底层实现是HashMap类的对象。
- 构造函数
初始容量:默认是16
加载因子:默认是0.75
也可以指定初始容量和加载因子。
- 遍历
- foreach
- 迭代器
1.3.4 LinkedHashSet类
底层是哈希表+链表实现
链表确定迭代的顺序。就是插入的顺序。先插入的元素遍历时先取出来
1.3.5 TreeSet类
底层是TreeMap,Tree Map的底层是红黑树。
红黑树BST(Binary Sort/Search Tree),这棵树上的元素是有顺序的,他还是一个自平衡的BST,添加或删除节点时,调整树的结构,保持平衡。
平衡二叉树:根节点左右两棵子树高度差不大于1.左右子树也是平衡的。
排序:自然顺序或重写compareTo方法,或Comparator方法。
TreeSet集合中的元素要求必须能够比较大小。
- 对象提供对Comparable的接口实现。重写compareTo方法。
- 创建TreeSet时,指定一个比较器Comparator,指定元素之间比较大小的方式
- A>B B>C => A>C
1.4Map接口
Map表示一个键和值之间的映射关系。
数组\List集合:通过下标找到元素。
Map集合:通过Key找到元素 键值对
1.4.1特点
特点:
- 键不能重复。
- 每个键只能有一个值
- 里面的元素是键值对映射的
1.4.2哈希表
哈希表:HashTable 键和值之间的映射关系叫做哈希
哈希中的映射关系通过一个哈希函数来进行
将Key通过一定的运算,得到一个哈希码或哈希地址
哈希函数:
- 直接定址法:哈希函数为一次函数,H(key)==key或H(key)=a*key+b;
其中H(key)表示关键字为key对应的地址
- 数字分析法:有多位字符或数字组成,考虑抽取其中两位或多位作为关键字对应的地址
- 平方取中法:对关键字做平方操作,去中间几位作为哈希地址。比较常用
- 折叠法:将关键字分割成位数相同的及部分,去及部分叠加和作为哈希地址,适用于关键字位数较多的情况。
- 除留余数法:若已知整个哈希表的最大长度m,可以取一个不大于m的数P,然后对关键字做取余运算,P可以为不大于m的质数或者不包含小于20的质因数合数
- 随机数法:取关键字关键字的一个随机函数值作为哈希地址,适用于关键字长度不等的情况。
哈希冲突:
- 开放地址法:线性探测、二次探测、伪随机数
- 再哈希法:再执行另一个哈希函数
- 链式地址法:桶+链表
- 公共溢出区:
Java用链地址法解决哈希冲突
1.4.3 HashMap类
1.特点:
基于哈希表
允许null和null键
不同步,线程不安全
元素是无序的。
2构造
初始容量:默认是16。也可以单独设置
桶的数量。用来表示桶的数组的长度。
加载因子:默认是0.75。也可以单独设置
用于扩容。当前Map中的元素,当到了当前容量*0.75,此时需要扩容
扩容时,新的容量=老的容量<<1
假设一个HashMap初始容量为16,则达到12时开始扩容,扩容后容量*2,左移一位,扩容为32.
HashMap中的每一个元素都会构造成Node,这些元素都保存在一个Node类型的数组中,这个数组就是桶。
桶中的元素,不是独立的单个元素,而是链表的形式。
当出现哈希冲突时,冲突元素将会与原来的元素组成一个链表。如果链表太长,查找效率就会降低。
HashMap的底层实现
底层由数组+链表+红黑树组成。
为什么要有链表?解决hash冲突,链式地址法
为什么要用红黑树?链表过长,导致效率太低,提高查找效率。
在JDK7以后,当HashMap中的元素数量达到64时,并且链表长度达到8时,链表将转换成红黑树(O(logn)),来提高查询效率。红黑树节点减少到6时,此时会再次转换成链表。红黑树可以通过节点颜色记录一些平衡的信息。
- 红黑树
它是一颗自平衡的二叉排序树,性能比AVL高。
通过红黑树的结点颜色,记录一些平衡信息。
特点:
- 结点要么是红要么是黑
- 根节点是黑色
- 叶子结点都是null
- 每个红色结点的子结点一定是黑色
- 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
变化:
- 插入结点初始都是红色
- 根结点是黑色
- 当前结点父结点是黑色,这种情况下不会破坏红黑树,不需要进行任何操作
- 当前结点父结点是红色的,并且叔叔节点也是红色的,将当前结点父结点和叔叔结点涂黑,祖父节点涂红
- 当前结点父结点是红色的,叔叔结点是黑色的(叔叔节点为null):
- 父结点是爷爷节点的左结点,当前结点是父结点的左结点(LL)
父结点变黑,爷爷节点变红,爷爷右旋转。
- (LR)父结点左旋,在进行LL
- RL 父结点右旋,在进行RR
- RR 父结点变黑,爷爷节点变红,爷爷节点左旋
- 构造方法:
HashMap<键泛型,值泛型> 对象名 = new HashMap<>();
- 方法
1)对象名.put(键,值) 添加键值对
2)对象名.clear() 清除所有键值对
3)对象名.replace(键,值) 替换某个键的值
4)对象名.clear() 清除所有键值对
5)对象名.remove() 删除某个键对应的值
6)对象名.get(键) 查询键对应的值
7)对象名.keySet() 查询所有key
8)对象名.values() 查询所有值
9)对象名.entrySet() 查询所有键值对
- 遍历
map集合不实现迭代器(Iterator),只能通过间接遍历
1.4.4 Hashtable类
1.特点
不允许使用null值和null键
同步,线程安全
操作方法上都加了synchronized修饰
1.4.5 LinkedHashMap类
基于哈希表+链表。
链表用于保存Key加入的顺序
迭代Key时,会按照加入的顺序进行迭代。
1.4.6 TreeMap类
基于红黑树
TreeMap中的键值对会根据Key的大小进行排序。
Key必须能够比较大小,要么有自然顺序,要么实现了Comparable接口,要么提供Comparetor比较器
ConcurrentHashMap类
基于哈希表的。
Concurrent:同时发生的,并发的。
这是一个线程安全的
Synchronized关键字,锁的是其中的一个Node,其中的一个桶。使用的是分段锁。允许多个线程锁不同的桶,所以效率更高
而HashTable锁的整个Map。使用的全局锁,效率低