专业的JAVA编程教程与资源

网站首页 > java教程 正文

② JAVA—容器(2)

temp10 2025-01-04 23:13:39 java教程 11 ℃ 0 评论

1. List与Set的区别

2. HashMap是线程不安全的,说明HashMap为什么是线程不安全。

② JAVA—容器(2)

3. HashMap的扩容过程,初始容量为16,加载因子是0.75,扩容为2倍

4. HashMap初始容量为什么是2的n次幂及扩容为什么是2倍的形式

5. HashMap在JDK1.7与1.8版本的区别

6. 集合框架底层数据结构

7. Java集合的快速失败机制fast-fail

8. Hash算法

9. 为什么不使用hashCode()获得的hashCode值来作为table的下标

10. try catch finally代码块的执行顺序


1. List与Set的区别

  • 1)List与Set都继承于Collection接口
  • 2)List是有序的,允许存储重复了;Set是无序的,数据不允许重复,Set的数据存放是通过hashCode()决定的。
  • 3)List支持下标遍历也支持迭代遍历,Set只支持迭代遍历
  • 4)List与数组类型,可以动态增长,查询效率比较高,删除与添加元素效率低
  • 5)Set底层是双向列表,查询效率较低,删除与添加元素效率高

2. HashMap是线程不安全的,说明HashMap为什么是线程不安全。

  • 存在线程A、B都向hashMap中插入元素,线程A对象进行添加的时候,经过hashCode()计算得到相应的hash值,判断发现对应的位置上没有元素,线程A在进行插入操作前,系统将CPU分配给了线程B,线程B的对象进行添加操作,对象经过hashCode()计算获取的hash值与A对象的相同,判断对应位置是否有对象时,发现还不存在对象,这是线程B直接在对应位置添加对象,操作结束后CPU分配给线程A,线程A继续添加对象操作,会直接将线程B的对象覆盖掉,所以HashMap是线程不安全的。
  • 在HashMap扩容的时候也会存在数据不一致情况,因为扩容是把一个数组的对象复制到另一个数组中。

3. HashMap的扩容过程,初始容量为16,加载因子是0.75,扩容为2倍

  • 当向容器中添加元素的时候,如果容器中元素的个数超过阈值,会触发扩容操作。
  • 阈值=容器容量*加载阈值
  • HashMap的扩容是使用一个新的数组来替代老的数组

4. HashMap初始容量为什么是2的n次幂及扩容为什么是2倍的形式

  • 在HashMap添加元素调用putVal()方法的时候,会使用hash&(n-1)方法进行与运算来计算对象在hashMap中存放的位置。 在调用resize方法的时候,会新建一个tab,遍历来tab,然后调用e.hash&(newCab-1)获取元素在新容器中的位置。
  • 两个方法的主要代码是hash&(n-1)的与运算操作,通过与运算操作可以使元素分布的更加均匀,可以减少hash碰撞。

5. HashMap在JDK1.7与1.8版本的区别

  • HashMap 1.7版本底层是数组+链表
  • HashMap 1.8版本底层是数组+链表或者红黑树 当链表的个数大于8的时候转换为红黑树,红黑树的节点小于6的时候变为链表

6. 集合框架底层数据结构

List:

  • ArrayList:Object数组
  • LinkedList:双向链表
  • Vector:Object数组
  • Stack:Object数组

Set:

  • HashSet:底层是HashMap
  • TreeSet:红黑树
  • LinkedHashSet:继承于HashSet,底层实现为LinkedHashSet

Map:

  • HashMap:JDK1.7 数组+链表,JDK1.8 数组+链表(红黑树)
  • HashTable:数组+链表
  • LinkedHashMap: 数组+链表(红黑树)
  • TreeMap:红黑树

7. Java集合的快速失败机制fast-fail

  • fast_fail是JAVA容器的一种错误检测机制,当多个线程对同一个容器进行操作的时候,容易产生fast-fail机制。
  • 原因:迭代器在遍历的时候会直接访问容器中的元素,而且在遍历过程中会生成一个modCount对象,如果在遍历过程中有其它线程修改了容器的结构就会改变modCount值,当在迭代器调用next()或hashNext()方法的时候,会进行modCount和expectedModCount值比较,如果两者不相等,会报ConcurrentModificationException异常
  • 解决方法:1)在遍历过程中,对于每个可以修改modCount的方法都加上synchronized方法 2) 使用CopyOnWriteArrayList代替ArrayList。

8. Hash算法

  • 哈希算法就是将任意长度的二进制值映射为固定较小长度的二进制值,这个值就是哈希值。
  • Hash值是获取一个32位的int值,然后进行高16与低16进行异或操作。

9. 为什么不使用hashCode()获得的hashCode值来作为table的下标

  • hashCode()返回的int整数类型,范围为-2^31至2^31-1,而HashMap的范围为16至2^30,如果使用hashCode()返回获取的值来作为下标,有可能hashCode()获取的值不在HashMap的范围之内,而且系统也难以提供那么多的空间。
  • 解决办法:1) HashMap实现了自己的hashCode()方法,经过多次扰动和高低位的异或操作,使数据分布的更加均匀,减少hash碰撞。 2)在保证数据长度是2的幂次方的时候,使用hash()方法获取的哈希值与数组长度减一值进行与运算hash&(n-1),这样操作的好处是:①比取余更加高效 ②只有当长度为2的幂次方时,hash&(n-1)才等价于h%length ③解决了哈希值与数组大小不匹配的问题

10. try catch finally代码块的执行顺序

  • 1) 在try中存在return操作,会先将return中结果存储起来,再执行finally中的操作,如果finally中修改了return结果,还是以try中的结果为主,如果返回的是引用类型,那么会返回finally中修改后的结果。
  • 2) 如果在try与finally中都存在return,那么以finally中的return为主。
  • 3)不论try中的操作,finally都会执行。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表