List、Set、Map的详解

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为了解决链表深度过深导致访问效率低的问题,底层结构变为:数组+单向链表+红黑树的结构。
  1. 什么时候创建数组?

    当Map对象调用put方法时创建数组,默认初始容量为16

  2. 什么时候扩容

    每次put方法添加元素完成后,都会判断数组是否需要扩容,减少哈希碰撞,,每次容量扩容会重新计算所有key的hash值

    map的负载因子为0.75,默认初始容量为16,当数组中元素个数达到12个时,就需要扩容。

  3. HashMap底层为什么使用hash算法,将hashcode方法的值右移16位

    hashcode相对而言容易出现哈希碰撞,hash算法可以减少哈希碰撞的次数,使键均匀的散列在连续而有限的存储空间,提高访问效率

  4. Map存放元素的过程,即调用putVal方法的过程:

    (1)判断数组上挂的桶是否为空,如果为空则初始化数组

    (2)判断key是否发送哈希碰撞,如果没有则直接把entry放在这个位置

    (3)如果发送哈希碰撞,判断是红黑树还是链表,如果是红黑树就以红黑树的方式把entry放入红黑树中。如果是链表,就需要先判断链表长度是否超过8,如果超过8就转为红黑树,以红黑树的方式放值;如果没有超过8就直接挂在链表后面

    (4)放值时需要判断key是否重复,重复则把原来的值覆盖掉,把返回原来的值

    (5)判断数组是否需要扩容

  5. 获取Map元素的过程

    (1)对key进行hash运算,确定entry在数组中的位置,判断桶是否为空,如果为空,则返回null

    (2)如果桶不为空,先检查第一个元素的key是否相同,相同则返回第一个元素;

    (3)如果与第一个元素key不相同,则判断桶是红黑树还是链表,然后再各自的方式进行查询,返回查询结果

  6. 什么时候链表转换为红黑树

    当链表长度大于等于8时,链表转换为红黑树,时间复杂度由线性阶O(n)变为对数阶O(logn),数据量大时红黑树查询效率高于链表

  7. 什么时候红黑树转换为链表

    当删除map中元素时,红黑树上的元素个数小于等于6时,就需要将红黑树转为链表

  8. 遍历map的两种方式:map.keySet map.entrySet【效率高】


  • HashMap、Hashtable、ConcurrentHashMap的区别:

    1. **ConcurrentHashMap:**JDK1.7时底层是数组+分段锁,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问

      JDK1.8时底层是数组+链表+红黑树,内部大量采用CAS操作

      **HashMap:**JDK1.7是基于数组和单向链表实现,DK1.8是基于数组+单向链表+红黑树的结构。

      **Hashtable:**底层原理与HashMap类似,只是所有方法都加了同步锁

    2. HashMap线程不安全,效率高;

    ​ Hashtable线程安全,内部的方法基本都是synchronized修饰,但效率低,锁死整个数组,串行操作

    ​ ConcurrentHashMap 线程安全的,是基于lock操作的,效率鉴于HashMap和Hashtable之间

    ​ 多线程环境下,我们一般使用ConcurrentHashMap

    1. 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倍。实际就是新建一个数组,将原数组的元素复制过来。
  • 哈希表中包含一个数组,和解决哈希冲突的数据结构【哈希函数+解决哈希冲突的方法】

    常用解决哈希冲突的方法:

    1. 数组中找一个新的空闲位置存放元素
    2. 数组+链表
    3. 数组+链表+红黑树

红黑树(了解)

树:有多个节点组成的有层次结构的集合

森林:多颗树组成的集合

根节点:树有有且只有一个根节点

子树:根节点有n个子节点,每个子节点及其子子孙孙节点组成的集合叫子树,这里就是有n个子树

叶子节点:无子节点的节点

二叉树:根节点、左子树、右子树三部分组成

平衡二叉树:二叉树左右子树的高度最多相差1

​ 左子树节点集合的平均值小于根节点,右子树的节点集合的平均值大于根节点

红黑树:平衡二叉树的一种

  1. 根节点为黑色
  2. 节点颜色只能为黑色和红色
  3. 红色节点不能连续
  4. 任意节点到叶子节点,黑色节点个数相同

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

栈的出口和入口都是栈的顶端

压栈:存元素,从栈顶往下

弹栈:取元素,从下往栈顶

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值