Collection
- Collection接口是Iterable接口的子接口
- Collection接口的三个常用子接口:List(列表)、Set(集合)、Queue(队列)
- Queue接口(单向队列)的子接口:Deque(双向队列),Deque的常用实现类:ArrayQueue、LinkedList
List
- List接口的常用实现类:ArrayList、LinkedList、Vector(已经被ArrayList取代)
- LinkedList实现了List、Deque两个接口,因此我们使用队列一般多使用LinkedList
- ArrayList和Vector的区别:ArrayList线程不安全,Vector线程安全,二者底层结构相同
- ArrayList和LinkedList的区别:
- ArrayList底层使用的是动态数组,LinkedList底层使用的是双向链表
- ArrayList查询效率比较快,可以使用索引随意访问,而增加、删除效率比较慢。
- 数组在内存中是一块连续的内存的,查询根据索引索引直接访问,但插入或删除时会创建新数组,将原数组的元素拷贝到新数组
- LinkedList增加、删除效率比较快,而查询效率比较慢。
- LinkedList不要求内存是连续的,多个节点之间通过地址链接,单个节点中存放上一个或下一个元素的地址。查询是从头开始,逐个遍历节点。
- 插入和删除,只需要改变上一个节点存放的地址,增加元素中存放下一个元素的地址
Set
- Set接口的常用实现类:HashSet、TreeSet(间接实现类)、LinkedHashSet
- Set接口的子接口为:SortedSet,所有有序的集合都实现了SortedSet接口,如TreeMap
- LinkedHashSet实现了Set接口,继承了HashSet类
- HashSet、LinkedHashSet底层使用的是HashMap,TreeSet底层使用的是TreeMap
Map
- Map接口的五个常用实现类:HashMap、TreeMap、LinkedHashMap、Hashtable、ConcurrentHashMap
- Map接口的子接口为:SortedMap(TreeMap是其实现类)、ConcurrentMap(ConcurrentHashMap是其实现类)
- Properties是Hashtable类的子类,键和值只能为String类型
HashMap底层结构:
- JDK1.7是基于数组和单向链表实现,每个元素为一个Map.Entry类型的键值对对象。数组扩容后会rehash,将长链表拆分为短链表放在空桶位置。
- JDK1.8为了解决链表深度过深导致访问效率低的问题,底层结构变为:数组+单向链表+红黑树的结构。
-
什么时候创建数组?
当Map对象调用put方法时创建数组,默认初始容量为16
-
什么时候扩容
每次put方法添加元素完成后,都会判断数组是否需要扩容,减少哈希碰撞,,每次容量扩容会重新计算所有key的hash值
map的负载因子为0.75,默认初始容量为16,当数组中元素个数达到12个时,就需要扩容。
-
HashMap底层为什么使用hash算法,将hashcode方法的值右移16位
hashcode相对而言容易出现哈希碰撞,hash算法可以减少哈希碰撞的次数,使键均匀的散列在连续而有限的存储空间,提高访问效率
-
Map存放元素的过程,即调用putVal方法的过程:
(1)判断数组上挂的桶是否为空,如果为空则初始化数组
(2)判断key是否发送哈希碰撞,如果没有则直接把entry放在这个位置
(3)如果发送哈希碰撞,判断是红黑树还是链表,如果是红黑树就以红黑树的方式把entry放入红黑树中。如果是链表,就需要先判断链表长度是否超过8,如果超过8就转为红黑树,以红黑树的方式放值;如果没有超过8就直接挂在链表后面
(4)放值时需要判断key是否重复,重复则把原来的值覆盖掉,把返回原来的值
(5)判断数组是否需要扩容
-
获取Map元素的过程
(1)对key进行hash运算,确定entry在数组中的位置,判断桶是否为空,如果为空,则返回null
(2)如果桶不为空,先检查第一个元素的key是否相同,相同则返回第一个元素;
(3)如果与第一个元素key不相同,则判断桶是红黑树还是链表,然后再各自的方式进行查询,返回查询结果
-
什么时候链表转换为红黑树
当链表长度大于等于8时,链表转换为红黑树,时间复杂度由线性阶O(n)变为对数阶O(logn),数据量大时红黑树查询效率高于链表
-
什么时候红黑树转换为链表
当删除map中元素时,红黑树上的元素个数小于等于6时,就需要将红黑树转为链表
-
遍历map的两种方式:map.keySet map.entrySet【效率高】
-
HashMap、Hashtable、ConcurrentHashMap的区别:
-
**ConcurrentHashMap:**JDK1.7时底层是数组+分段锁,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问
JDK1.8时底层是数组+链表+红黑树,内部大量采用CAS操作
**HashMap:**JDK1.7是基于数组和单向链表实现,DK1.8是基于数组+单向链表+红黑树的结构。
**Hashtable:**底层原理与HashMap类似,只是所有方法都加了同步锁
-
HashMap线程不安全,效率高;
Hashtable线程安全,内部的方法基本都是synchronized修饰,但效率低,锁死整个数组,串行操作
ConcurrentHashMap 线程安全的,是基于lock操作的,效率鉴于HashMap和Hashtable之间
多线程环境下,我们一般使用ConcurrentHashMap
- HashMap允许null键和null值,Hashtable、ConcurrentHashMap键和值都不能为null
-
-
LinkedHashMap、HashMap的区别:
HashMap是无序的,LinkedHashMap是有序的,能保证元素的插入顺序和输出顺序一致
LinkedHashMap线程不安全,实现了Map接口,继承了HashMap类,底层原理:数组+双向链表
-
HashMap判断元素的键是否重复的方法:先调用hashCode方法,再调用equals方法
两个对象哈希值相同,后添加的原元素调用equals方法,返回true则元素重复,返回false则元素不重复
面试题:两个对象equals方法判断相同,哈希值一定相同
-
List集合可以调用contains方法来判断元素是否重复的原理:实际是调用equals来判断
-
哈希表如何存储自定义对象:
- 覆写自定义对象的hashCode方法:如果对象属性值相同,则哈希值相同
- 哈希值相同,但仍然无法保证对象相同,因此需要覆写equals方法,判断各属性是否相等
-
TreeMap和HashMap的异同:
-
TreeMap和HashMap都是Map接口的实现类,两者都是线程不安全的
-
HashMap是无序的,TreeMap是有序的
-
TreeMap的底层是红黑树,HashMap的底层是哈希表,HashMap比TreeMap的查询和增删的效率高,只要需要排序的时候才用TreeMap
-
TreeMap键不能null,HashMap的键可以为null
-
TreeMap、TreeSet去重复使用的是compareTo方法或compare方法
HashMap、TreeSet去重复使用的是hashCode方法和equals方法
-
-
TreeMap和LinkedHashMap的区别:
LinkedHashMap是根据插入先后顺序确定遍历的顺序
TreeMap是根据Key进行自然排序或定制排序
哈希表
-
哈希表(也叫:散列表,Hash table):将关键字通过哈希函数映射到哈希表中的一个位置(槽位)
- 实际是关键字和已分配的有限连续内存空间建立映射关系
-
槽:哈希表中用于保存数据的一个单元
-
哈希函数:关键字和槽建立映射关系的函数,哈希函数返回的哈希值只能是整数
好的哈希函数:减少哈希冲突,使所有元素的映射尽可能均匀地分布在有限个连续的槽位上
-
哈希冲突:不同关键字的哈希值可能相同,因此不同关键字经过哈希函数可能会映射到相同位置(槽位)
-
同义词:哈希表中关键字不同但哈希值相同的元素
-
桶:单个槽位上的链表或红黑树以看成一个桶 【哈希表为数组+链表或数组+链表+红黑树】
桶的缺点:当过多的哈希冲突发生在同一槽位时,“桶”将会变得越来越深,造成访问这个位置的元素所需要的时间越来越多。
-
加载因子:哈希表中元素的填满的程度
加载因子越大,填满程度越高,空间利用率越高,但哈希冲突的机会越大,查找效率越慢;
加载因子越小,填满程度越地,空间利用率越低,但哈希冲突的机会越小,查找效率越快。
因此必须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷
- HashMap默认容量为16,加载因子默认为0.75,当数组元素个数达到12个时数组就要扩容,跨容为原来的2倍。实际就是新建一个数组,将原数组的元素复制过来。
-
哈希表中包含一个数组,和解决哈希冲突的数据结构【哈希函数+解决哈希冲突的方法】
常用解决哈希冲突的方法:
- 数组中找一个新的空闲位置存放元素
- 数组+链表
- 数组+链表+红黑树
红黑树(了解)
树:有多个节点组成的有层次结构的集合
森林:多颗树组成的集合
根节点:树有有且只有一个根节点
子树:根节点有n个子节点,每个子节点及其子子孙孙节点组成的集合叫子树,这里就是有n个子树
叶子节点:无子节点的节点
二叉树:根节点、左子树、右子树三部分组成
平衡二叉树:二叉树左右子树的高度最多相差1
左子树节点集合的平均值小于根节点,右子树的节点集合的平均值大于根节点
红黑树:平衡二叉树的一种
- 根节点为黑色
- 节点颜色只能为黑色和红色
- 红色节点不能连续
- 任意节点到叶子节点,黑色节点个数相同
Comparator 接口
- 该接口的实现类,表示比较规则这类事物
- 其实现类需要覆写compare()方法,根据返回的正负来决定顺序,返回0则表示重复
Comparable接口
- 实现该接口的类,表示该类的实例可以比较大小,可以进行排序
- 其实现类需要覆写compareTo()方法,根据返回的正负来决定顺序,返回0则表示重复
自然排序和定制排序
TreeSet,TreeMap默认自然升序排列
- 自然排序:TreeSet的存储对象、HashMap存储对象的key,必须实现Comparable接口,覆写compareTo方法,根据返回值正负进行排序,返回值为0,则认为重复,不存入TreeSet,TreeMap
@Override
public int compareTo(User o) {
return 0;
}
- 定制排序:定制Comparator接口的实现类比较器,实现compare方法,根据返回值正负进行排序,返回值为0,则认为重复,不存入TreeSet,TreeMap
public class UserComparator implements Comparator<User> {
@Override
public int compare(User o1, User o2) {
return (int)(o1.getId()-o2.getId());
}
}
总结
-
List、Set、Map关于null的处理:
- List可以重复存放null
- HashSet只允许存放一个null,TreeSet不允许存放null
- HashMap允许null键和null值,TreeMap键不能为null,值可以为null
- HasTable(Properties)、ConcurrentHashMap键和值都不能为null
-
List、Set、Map的区别:
- List允许对象重复,有序集合
- Set不允许对象重复,无序集合
- Set是根据equals和hashcode方法来判断添加的元素是否重复
- 一个对象要存储到set中,必须覆写equals和hascode方法
- Map存放的是键值对,键不能重复,无序集合
TreeSet默认情况下是按字符的自然顺序升序排列
-
Collection继承了Iterable接口,可以迭代
Map接口不能迭代
-
ArrayList初始化容量10,HashMap初始化容量16
Iterator和Iterable
-
实现了Iterable接口的对象,才能使用foreach进行遍历
-
迭代器是一个对象,用于遍历和取出集合中的元素,不用关心集合内部结构
hasnext方法判断集合中是否还有元素,next方法在执行hasnext方法后取出元素
-
遍历过程中,修改集合的长度,会出现并发修改异常。
-
遍历过程中,无法修改元素的引用【修改语句不起作用,不报错】,但可以修改元素的属性值
常用的数据结构
堆栈、队列、数组、链表
堆栈:先进后出 FILO
队列:先进先出 FIFO
栈的出口和入口都是栈的顶端
压栈:存元素,从栈顶往下
弹栈:取元素,从下往栈顶