• java集合


    1. 继承体系
      • Collection、Map、List、Set、Queue
      • 是否有序、是否可重复、键值对
    2. 集合fail-fast机制
      • 错误检测机制, 防止对非线程安全集合的并发修改
      • ConcurrentModificationException
    3. 如何创建不可变集合
      • Collections#unmodifiableXxx
      • 进行修改操作时抛出异常UnsupportedOperationException
    4. 如何迭代时删除元素
      • 迭代器遍历删除
      • fori删除会导致有些元素访问不到; foreach删除抛出异常, 因为底层使用迭代器
    5. ArrayList和LinkedList区别
      • 数组、链表
      • 连续内存、常数时间内随机访问
    6. ArrayList和Vector区别
      • 是否线程安全、扩容策略
    7. Vector的size为什么要同步, 扩容策略2和1.5区别
      • ?
      • 倍增则均摊O(1)复杂度, 固定增长O(N)
      • 倍增导致下一次申请的内存必然大于之前所有分配总和, 可能无法复用
    8. 如何创建线程安全集合
      • Collections#synchronizedXxx
    9. List实现类
      • ArrayList、LinkedList、CopyOnWriteArrayList、Vector

    1. HashMap和Hashtable区别

      • 线程安全性、null键值
      • 哈希值, 前者重新计算
      • 扩容, 前者扩容为2倍+1, 后者2倍扩容.
      • 结构/解决哈希冲突办法, 前者数组+链表, 后者数组+链表+红黑树
    2. HashMap在JDK和JDK8区别

      • 数组 + 链表 + 红黑树
      • 插入改尾插, 不会出现多线程下循环链表的问题
      • 哈希值处理, JDK7不处理
    3. 基本原理?HashMap为什么使用数组+链表?哈希冲突的解决办法

      • 使用Node数组存储k-v值, Node实际是一个单向链表
      • 开放定址法、链地址法、再哈希法、公共溢出区
      • 开放定制: 线性探测、平方探测, ThreadLocal使用线性探测
      • 链地址法: 链表存储冲突元素
      • 再哈希法: 冲突则使用另一个哈希函数再哈希
      • 公共溢出区: 冲突的全部使用List存储, 那么若哈希冲突则需要遍历这个List
    4. LinkedList代替数组可以吗

      • 可以, 但是效率很低, 因为需要本质上是需要常数时间内的随机访问
    5. ArrayList可以吗

      • 不可以, 因为ArrayList的扩容固定了, 而HashMap要求倍增
    6. HashMap什么条件下扩容?为什么扩容2次幂?hash值计算&为什么?为什么默认装载因子0.75

      • 当容量到达 数组长度*装载因子 扩容
      • 为了便于使用位运算计算元素所在桶, 高效计算
      • hash ^ (hash >>> 16), 容量较少时利用到高位, 降低膨胀几率
      • 太大空间利用率低太小碰撞几率增加. 0.6-0.8合适, 取0.75折衷结果.
        • 若Hash分布均匀, 则碰撞符合泊松分布, 达到8个节点的概率小于千万分之一
    7. put流程

      • 数组为null或长度为0先初始化, 默认容量16
      • 计算哈希值计算索引, 没有碰撞则直接存储
      • 碰撞若第一个节点k相同直接替换val
      • 不同则判断节点类型, 分别插入树和链表, k同则值换
      • 若链表节点插入新节点, 若链表长度大于等于8且同容量大于等于64, 树化, 小于64扩容
      • 最后容量大于扩容阈值则扩容
    8. get流程

      • k计算hash值计算索引
      • 桶节点为null则没有值, 返回null
      • 否则第一个节点k值相等返回值
      • 否则对链表节点/树查找
    9. 哈希算法

      • 把大范围值映射到小范围(或固定长度小范围), 又称摘要算法Digest
      • MD4、MD5, MD Message Diagest, 输出长度16B. MD5输出较短, 短时间内破解是可能的, 不推荐使用
      • SHA-1, 数组20B, SHA-256输出32B, SHA-256输出64B
      • BASE64
      • 哈希算法输出越长, 越难产生碰撞, 越安全
      • 可以加盐, 也就是加上随机随机字符再进行哈希运算, 那么就难以逆推
        • 逆推: 计算常用密码的MD5, 然后根据MD5碰撞尝试是否为这个常用密码
        • 加盐: 碰撞出字符串, 我们也会加上盐, 加盐后MD5就比对不一样了
      • 文件校验: 是否被修改、文件是否一致
    10. String的哈希计算

      • 31为权, 对每一个字符进行加权多项式计算, 自然溢出值为hashCode
      • 31为质奇数, 运算可优化为位运算
      • 简单、运算块、分布较为均匀
      • 缓存hashCode + 不可变对象
      • 【原理】
    11. JDK8改了啥?为什么不直接使用红黑树?BST可以吗?阈值为什么是8?什么时候退化

      • 加了红黑树、哈希运算优化、链表尾插
      • 红黑树需要旋转平衡, 元素较少时实际效率差别不大, 较多时才能使用查询效率抵换平衡花费
      • BST可能退化为链表
      • k随机均匀、装载因子0.75, 则一个桶中元素遵循λ为0.5的泊松分布, 8时概率低于千万分之一
      • 6, 有一个节点的缓冲, 避免频繁转换
    12. 并发问题

      • JDK7多线程扩容死循环, JDK8没有
      • put丢失无法get
      • 使用线程安全集合Map
    13. 什么键适合作为k

      • 不可变对象, String、包装类
      • 键可以为null
      • 可变k可能导致无法get出来
    14. 自定义对象作为键要如何处理

      • 最好是不可变对象, final 修饰实例属性, 构造函数初始化
      • hashCode 和 equals 进行重写
      • hashCode 缓存
      • 不提供getter
      • 传入可变类进行初始化则需要clone
    15. Map实现类

      • 有序?线程安全?按什么排序?
      • HashMap、LinkedHashMap、TreeMap、WeakHashMap
      • ConcurrentHashMap、ConcurrentSkipListMap
      • Collections.synchronizedMap、unmodifiableMap
      • 强软弱虚引用
    16. 缓存淘汰策略

      • LRU: 最近最少使用, 最长时间没有访问的. LinkedList 重写 removeEldestEntry 根据策略移出元素
      • LFU: 最不经常使用的
    17. ConcurrentHashMap

      • JDK7的锁Segment, 每个Segment多个桶, JDK8直接锁桶, 丢弃Segment的实现并加入红黑树, 同时使用synchronized而非Lock
      • JDK8没有哈希冲突时直接CAS, 否则才会锁住整个桶
      • 不支持键值的null
      • 写加锁, 读不加锁
      • Hashtable强一致性, ConcurrentHashMap 若一致性
      • 协助扩容
        • 认领一个区间进行扩容, 会反应到实例变量中, 因此每一个线程会领取到合适的区间
          • 最小16个桶
          • len / 8 / NCPU, 若NCPU为1则全部桶
          • 当处理完自己的区间, 回去看一下是否领取完毕, 没有则再次领取一个区间
        • 扩容时节点新new, 所以不影响原数组, 不影响get操作. 已处理好的则一个新的数据类型节点转发到新的数组桶
    18. ConcurrentSkipListMap

      • 跳表, 可以进行二分查找的链表
      • 在链表上面添加多级索引, 空间换时间, 最底层所有元素双向链表, 可范围查找
      • 插入元素时, 随机数找到其需要建立索引等层级, 然后插入各层位置
        • 随机数 & 0x80000001: 非负偶数才处理
        • 判断值从第二为开始有几个连续1, 就几层+1, 最底层1层, 最小也是1层., 0110110为3, 01100为1
        • 超过现有层级则最多超过1层
    19. 线程安全的Map都不允许null值null键

      • getKey得到了值并不能判断val是null还是没有此元素, 而单线程不担心因为本来就不不在多线程下使用
    20. 什么时候使用TreeMap

      • 当需要有序遍历的时候
    21. Set实现类

      • HashSet、LinkedHashSet、TreeSet
      • CopyOnWriteArraySet、ConcurrentSkipListSet
      • Collections.newSetFromMap 转 Map 为 Set, 可获得 ConcurrentHashSet
    22. 并发队列: ArrayBlockingQueue、LinkedBlockingQueue、ConcurrentLinkedQueue 区别

      • ArrayBlockQueue: 定容, 一把锁 ReentrantLock + Condition 实现
      • ConcurrentLinkedQueue: 非定容, CAS + 重试 实现, 入队速度很快
      • LinkedBlockingQueue: 定容(MAX), 两把锁实现, 出队入队互不影响

    1. Queue实现类

      • PriorityQueue 【非定容, 不允许为null】

      • 小根堆实现, 及返回最小元素
        +-----------------------------+
        | 操作 | 抛出异常 | 返回特定值|
        +-----------------+-----------+
        | 入队 | add | offer |
        | 出队 | remov | poll |
        | 队首 | element | peek |
        +------+----------+-----------+

      • ConcurrentLinkedQueue【非定容】

        • 非阻塞队列
        • 线程安全
      • ArrayDeque

      • LinkedList

      • 非线程安全的均非定容

    2. 阻塞队列

      • ArrayBlockQueue 【定容, 必须指定容量, 不允许为null】

        • 只是用了一把锁来控制入队与出队
        • 可传入使用公平锁和非公平锁, 默认非公平
          +-----------------------------+----------------+
          | 操作 | 抛出异常 | 返回特定值| 阻塞 | 超时 |
          +-----------------+-----------+-------+--------+
          | 入队 | add | offer | put | offer |
          | 出队 | remov | poll | take | poll |
          | 队首 | element | peek | | |
          +------+----------+-----------+-------+--------+
      • LinkedBlockingQueue【定容, 默认容量整型最大值, 不允许为null】

        • 两把锁分别锁入队和出队
      • Synchronous【没有被消费前阻塞, 不允许为null】

        • [ˈsɪŋkrənəs] 同步的, 同时的
        • 无缓冲阻塞队列
        • 两线程间移交元素
        • 实际会缓冲, 比如生产者多, 则内部存储起来, 且阻塞生产者, 直到被消费
      • PriorityBlockingQueue【非定容, 不允许为null】

      • LinkedTransferQueue【不允许为null】

        • LinkedBlockingQueue、SynchronousQueue(公平模式)、ConcurrentLinkedQueue三者的集合体
        • 生产者可以控制是否阻塞
        • 消费者还是会阻塞
      • DelayQueue【非定容, 不允许为null】

        • 阻塞队列
        • SchduledThreadPoolExecutor不是直接使用, 而是重新进行实现的

      -阻塞队列与非阻塞队列的区别
      - 是否提供阻塞方法: put、take

    3. 线程池

      • Executors.newSingleThreadExecutor: 1核心线程1最大线程 + LinkedBlockingQueue
        • 问题: MAX定容, 任务过多导致OOM
      • newFixedThreadPool: 核心最大一样, LinkedBlockingQueue
        • 问题: MAX定容, 任务过多导致OOM
      • newCachedThreadPool: 无核心线程, 最大MAX + SynchronousQueue + 60s超时
        • 问题: MAX线程数导致OOM
      • DelayedWorkQueue: 核心设置 + 最大MAX + DelayedWorkQueue
        • 问题: 最大线程数MAX, 但是 DelayedWorkQueue 最大MAX
        • 因此问题还行MAX队列

    【List】
    【没有固定容量的】
    ArrayList
    - 默认容量10
    - 传入容量立即分配空间
    - 1.5倍扩容
    - 不会默认缩容
    - 常数时间内的随机访问
    - 适合尾部增删
    LinkedList
    CopyOnWriteArrayList
    - 读写分离, 写时复制, 阻塞写不阻塞读
    - 弱一致性, 不保证实时一致性, 只保证最终一致性
    - ArrayList的线程安全版本
    - 适合读多几乎不写的场景
    Vector


    【Map】
    【无序、有序、线程安全】
    +-------------------------+--------------------------------------+
    | Map | 键值 |
    +-------------------------+--------------------------------------+
    | HashMap | 均允许为null |
    | LinkedHashMap | 均允许为null | 继承HashMap
    | WeakHashMap | 均允许为null |
    | TreeMap | 有比较器则允许, 没有则k不能为null |
    | ConcurrentHashMap | 均不允许为null | 固定装载因子
    | ConcurrentSkipListMap | 均不允许为null |
    +-------------------------+--------------------------------------+
    HashMap
    - 允许null键null值
    - 数组 + 链表 + 红黑树
    - bucket桶
    - 容量必须为2的次幂
    - 默认装载因子0.75
    - 树化阈值8 + 64桶
    - 链化阈值6
    - Node单链表节点, newNode创建, 便于LinkedHashMap扩展维护双链表
    - hash函数特殊
    - 不会立即开辟空间
    - put可以相同时替换key并调用 afterNodeAccess, 便于LinkedHashMap扩展维护LRU
    - put结束后调用了 afterNodeInsertion(removeEldestEntry), 便于LinkedHashMap扩展维护LRU

    LinkedHashMap
    - 允许null键null值
    - 继承 HashMap
    - 重写 newNode 维护双链表
    - 能按照插入顺序排序, 也能按照访问顺序排序, 可以实现LRU缓存

    TreeMap
    - 继承 SortedMap、Navigable, 注意 LinkedHashMap 没有
    - 红黑树(红色或黑色、Root黑色、红色孩子黑色、任意节点到所有叶子节点路径上黑色节点数相同、BST)
    - 有比较器则允许null键, 否则不允许

    ConcurrentHashMap
    - 不允许null键null值
    - HashMap线程安全版本, 数组 + 链表 + 红黑树
    - 相比Hashtable效率极大提高, 不过不是强一致性的(Hashtable的get和size均同步)
    - synchronized + CAS + 自旋 + 分段锁
    - size存储类似 LongAddr, 分段存储, 不同线程哈希到不同的段
    - 读操作不加锁, 不是强一致性的

    ConcurrentSkipListMap
    - 键值均不允许为null
    - 跳表实现, 实质是可以进行二分查找的有序链表, 原链表基础上加上多级索引(缓存), 通过索引实现快速查找
    - Redis使用跳表实现有序列表是因为跳表实现较为简单易读, 且范围查询效率较高
    - List存储
    - 索引层数决定
    - ThreadLocalRandom.nextSecondarySeed 随机数
    - 正偶数
    - 有多少个1就建立几层
    - 超过现有最高层则最多高一层且头结点也新建一层


    【Set】
    +-----------------------+-------------+
    | Set | 元素 |
    +-----------------------+-------------+
    | HashSet | 允许为null | HashMap字段, Object值, 没有get, 三个参数的构造函数使用LinkedHashMap
    | LinkedHashSet | 允许为null | 继承HashSet, 这里HashSet的三参构造函数有了用处, 基本上没有自己的API, 不支持访问顺序排序
    | TreeSet | 有比较器允许| 字段默认TreeMap, 也可传NavigableMap子类(ConcurrentSkipListMap)
    | ConcurrentSkipListSet | 不允许 | ConcurrentSkipListMap字段
    | CopyOnWriteArraySet | 允许 | CopyOnWriteArrayList字段
    +-----------------------+-------------+
    LinkedHashSet并没有实现SortedSet接口, LinkedHashMap也没有SortedMap


    【Queue】
    +-----------------------------+----------------+
    | 操作 | 抛出异常 | 返回特定值| 阻塞 | 超时 |
    +-----------------+-----------+-------+--------+
    | 入队 | add | offer | put | offer |
    | 出队 | remov | poll | take | poll |
    | 队首 | element | peek | | |
    +------+----------+-----------+-------+--------+

    【均不允许元素为null】
    +---------------------------+--------------------------+
    | 队列 | 是否定容 |
    +---------------------------+-------------- -----------+
    | ArrayBlockingQueue | 是 |
    | LinkedBlockingQueue | 定容, 默认MAX |
    | SynchronousQueue | 双方均会阻塞 |
    | PriorityBlockingQueue | 否 |
    | LinkedTransferQueue | 消费者阻塞, 生产者可控 |
    | DelayQueue | 否 |
    | ScheXxx.DelayedWorkQueue | 否 |
    +---------------------------+--------------------------+
    +-----------------------+
    | Queue | 全不允许元素为null
    +-----------------------+
    | PriorityQueue | 小根堆, 非定容
    | ArrayBlockingQueue | 定容, 必须传入容量, 立即扩容, 出队入队一把锁
    | LinkedBlockingQueue | 定容, 默认容量MAX, 出队入队分别锁, 效率较高
    | SynchronousQueue | 交换元素
    | PriorityBlockingQueue | 非定容, 小根堆
    | LinkedTransferQueue | 相对SynchronousQueue效率更高
    | ConcurrentLinkedQueue | 线程安全, 但不是阻塞队列, 非定容
    | DelayQueue | PriorityQueue + 锁实现
    | ArrayDeque | 循环数组实现双端队列
    | LinkedList | 双链表
    +-----------------------+

    ================================================================================

    1. 线程安全 Collections.synchronizedXxx
    2. 不可变 Collections.unmodifiableXxx
    3. Collections.newSetFromMap
    4. Collections.EMPTY_XXX
    5. 单个元素 Collections.singletonXxx
    6. N个重复元素的List Collections.nCopies

    LRU、LFU
    多少个1的位运算(与上数-1)
    LRU & LFU缓存机制的原理及实现 - 知乎 (zhihu.com)

    Redis LRU 算法和LFU算法 - 知乎 (zhihu.com)

    LRU(Least Recently Used):最近最少使用淘汰,最长没有访问

    LFU(Least Frequently Used):挑选最不经常使用的数据淘汰,访问次数最少

    哈希算法: 随机、MD4、MD5, 大范围映射到小范围

    面试 ConcurrentHashMap ,看这一篇就够了! - 知乎 (zhihu.com)

    为什么ConcurrentHashMap是弱一致的 | 并发编程网 – ifeve.com

    ThreadLocal
    - Thread 的实例变量 ThreadLocal.ThreadLocalMap
    - 单个 ThreadLocal 实例, 每个 ThreadMap<ThreadLocal实例, 值>
    - ThreadLocal如何实现线程安全: 每个 Thread 都有自己的 Map, 都有一个键为 ThreadLocal 实例, 于是一个 ThreadLocal 实例在多个
    Thread 中共存, 但是每个 Thread 中相同的 ThreadLocal 实例对应的值是多例的, 每个线程不一样
    - Map
    - 哈希冲突解决: HashMap 链地址, ThreadLocal 开放定址-线性探测
    - 2倍扩容, 易于计算 index
    - 阈值 2 / 3 = 0.666
    - 没有找到回收元素且当前元素数量 > 阈值, 则扩容 https://zhuanlan.zhihu.com/p/402281916
    - 内存泄露
    - Map 底层是 Entry, 键为 ThreadLocal 实例弱引用
    - 为什么是弱引用: 因为一般使用线程池, 那么 Thread 就是一个长生命周期对象, 若没有显式调用 remove, 则即便 ThreadLocal 实例不使用也无法被回收
    - ThreadLocal 在每次 set、get 赋值都会移除被回收的对象
    -- 问题: 线程池中, 一次使用了 ThreadLocal, 但是后面就不再使用了, 就产生了内存泄露
    - set、get、remove 已经见减少了内存泄露的可能性
    - 但是若 static 的 ThreadLocal, 则延长了生命周期
    - 或者说使用了 ThreadLocal, 后续又不再调用 set、get
    - 解决: 每一次使用完毕都 remove

  • 相关阅读:
    outlook2007邮件里的图片显示不出来
    MimeMessageHelper代码发邮件时,通过客服端登陆到邮箱,在已发送邮件里没有已经通过代码发送的邮件
    wordpress页面F12时源码多出的内容在index.php header.php找不到
    jackson-databind-2.2.3.jar,ackson-annotations-2.2.3.jar和jackson-core-2.2.3.jar下载
    系统分析师教程(张友生)高清pdf下载
    Sharepoint 恢复列表文件
    call this的范围
    吴裕雄--天生自然JAVA开发JSP-Servlet学习笔记:servlet获取表单请求参数并在客户端显示
    吴裕雄--天生自然JAVA开发JSP-Servlet学习笔记:session对象-属性可以在多个页面的跳转之间共享
    吴裕雄--天生自然JAVA开发JSP-Servlet学习笔记:response对象-增加带有中文的Cookie
  • 原文地址:https://www.cnblogs.com/chenxingyang/p/16114113.html
Copyright © 2020-2023  润新知