Java面试常考——集合

集合分为Collection / Map

在这里插入图片描述

  • Collection对象之间没有指定的顺序,允许有重复元素和多个null元素对象;

    它是Set和List接口的父类,是一种最通用型的集合接口。

集合的分类
  • List:有序列表(可以有重复元素)集合
  • Set:没有重复元素的集合
  • Map:通过键值对查找映射表的集合——需要排序用 TreeMap
集合细分
  • List——有序存储
    • LinkedList:双向链表结构,适合插入删除(线程不安全)
      • LinkedList 查找慢的原因是找每个元素要从头开始遍历
    • ArrayList:动态数组结构,适合随机访问(线程不安全)
    • Vector:数组结构,Stack 是 Vector 的实现类(线程安全)
  • Set——不可重复
    • 不可重复的实现:往HashSet中存放元素时,默认会对数据和集合中的数据进行比较,首先会比较它们的Hashcode
      • 如果 hashcode 不同,则直接插入
      • 当Hashcode相同时
        • 再进行equals()比较,如果返回true则证明两个元素相同,将不对该元素进行存放
        • 如果是false,则会进行散列,然后存放。
    • hashcode 的产生
      • 每个对象肯定有一个物理地址,将物理地址对应的整数通过 hash 函数,就得到一个 hashcode,也就是 hash 表中的位置(hashcode 值相同,说明在同一个散列存储结构中)
    • HashSet 使用 hash 表(数组)存储元素
      • 不需要自动排序就用LinkedHashSet
    • TreeSet 底层是 TreeMap,在 TreeSet 中存入数据,相当于存入 TreeMap 的 key 值,在 TreeSet 排序存储后,就有value 值了。
  • Map——key 值不可重复
    • HashMap 线程不安全,效率高,允许 key 和 value 为 null
    • HashTable 是线程安全的,这个类中一些方法加入了 synchronized 关键字
    • TreeMap ——非线程安全,基于红黑树对所有 key 排序
HashMap
HashMap 的数据结构
  • HashMap 在 JDK 1.8 以前的版本中,HashMap 的实现是数组+链表,当有大量元素时,链表会很长,遍历起来很麻烦
  • JDK 1.8 以后,HashMap的实现依靠 数组+红黑树数组+链表。添加元素时,若桶中链表元素超过8个,链表就会转换成红黑树;删除元素和扩容时,若桶中结构为红黑树并且树中元素较少时,进行剪枝或还原成链表(少于6个)。遍历查找的时间复杂度为 O(logn),性能较高。
HashMap 的工作原理
  • HashMap 数组中的每个元素都是链表,由Node内部类(实现Map.Entry<K,V>接口)实现,HashMap通过put&get方法存储和获取元素
  • 存储元素 put() 方法
    • 调用hash(K)方法计算K的hash值,然后结合数组长度,计算得数组下标
    • 调整数组大小(当容器中的元素个数大于capacity*loadfactor时,容器会进行扩容resize为2n)
    • 哈希碰撞
      • 如果K的hash值在HashMap中不存在,则执行插入,若存在,则发生碰撞;
      • 如果K的hash值在HashMap中存在,且它们两者equals返回true,则更新键值对;
      • 如果K的hash值在HashMap中存在,且它们两者equals返回false,则插入链表的尾部(尾插法)或者红黑树中。
  • 获取元素 get() 方法
    • 调用hash(K)方法(计算K的hash值)从而获取该键值所在链表的数组下标
    • 顺序遍历链表,equals()方法查找相同Node链表中K值对应的V值
HashMap 的线程不安全
  • 在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。
  • 在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
ConcurrentHashMap
ConcurrentHashMap 与 HashMap 的区别
  • ConcurrentHashMap 对整个桶数组进行了分割分段,然后在每一个分段上都用lock锁进行保护,线程安全(HashMap 线程不安全)
  • HashMap 键值对允许有 null,ConcurrentHashMap 都不允许。
ConcurrentHashMap 与 HashTable 的区别
  • 底层数据结构:JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用数组+链表/红黑二叉树,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
  • 实现线程安全的方式:
    • ConcurrentHashMap 使用分段锁,在 JDK1.7 时,ConcurrentHashMap 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 JDK1.8 时,用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作
    • HashTable 使用 synchronized (对对象加锁)来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
  • ConccurentHashMap 的默认并发度是16,根据用户设置16,32…
红黑树
  • 特点:
    • 每个节点非红即黑
    • 根节点总是黑色的
    • 如果节点是红色的,则它的子节点必须是黑色的(反之不一定)
    • 每个叶子节点都是黑色的空节点(NIL节点)
    • 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值