2018年Java面试题整理(持续整理中)

一.基础知识:

1)集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList;HashSet,TreeSet)

List:元素是有顺序的,元素可以重复因为每个元素有自己的角标(索引)
  |-- ArrayList:底层是数组结构,特点是:查询很快,增删稍微慢点,线程不同步:A线程将元素放在索引0位置,CPU调度线程A停止,B运行,也将元素放在索引0位置,当A和B同时运行的时候Size就编程了2.
  |-- LinkedList:底层使用的是链表数据结构,特点是:增删很快,查询慢。线程不安全,线程安全问题是由多个线程同时写或同时读写同一个资源造成的。
  |--Vector:底层是数组数据结构,线程同步,Vector的方法前面加了synchronized关键字,被ArrayList代替了,现在用的只有他的枚举。

Set:元素是无序的,且不可以重复(存入和取出的顺序不一定一致),线程不同步。set底层是使用Map实现的,故可以通过ConcurrentHashMap的方式变通实现线程安全的Set。
|--HashSet:底层是哈希表数据结构。根据hashCode和equals方法来确定元素的唯一性。
hashCode和equals:作用一样,都是用来比较两个对象是否相等一致。
equals比较的比较全面,而利用hashCode()进行对比,则只要生成一个hash值进行比较久可以了,效率高。
equal()相等的两个对象他们的hashCode()肯定相等,也就是equal()是绝对可靠的。
hashCode()相等的两个对象他们的equal()不一定相等,hashCode()不是绝对可靠的。

Map:这个集合是存储键值对的,一对一对往里存,而且要确保键的唯一性(01,张三)这样的形式打印出来就是  01=张三
|--HashTable:底层是哈希表数据结构,不可以存入null键和null值,该集合线程是同步的,效率比较低。出现于JDK1.0。线程安全,使用synchronized锁住整张Hash表实现线程安全,即每次锁住整张表让线程独占。
|--HashMap:底层是哈希表数据结构,可以存入null键和null值,线程不同步,效率较高,代替了HashTable,出现于JDK 1.2
|--TreeMap:底层是二叉树数据结构,线程不同步,可以用于对map集合中的键进行排序
ConcurrentHashMap:线程安全,允许多个修改操作并发进行,其关键在于使用了锁分离技术,它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的Hashtable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
当两个对象需要对比的时候,首先用hashCode()去对比,如果不一样,则表示这两个对象肯定不相等(也就不用再比较equal(0)了),如果hashCode()相同,再比较equal(),如果equal()相同,那两个对象就是相同的。
|--TreeSet:可以对Set集合中的元素进行排序(自然循序),底层的数据结构是二叉树,

2)HashMap的底层实现,之后会问ConcurrentHashMap的底层实现

HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。允许使用null值和null键。

HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。

HashMap是基于hash算法实现的,通过put(key,value)存储对象到HashMap中,也可以通过get(key)从HashMap中获取对象。

当我们使用put的时候,首先HashMap会对key的hashCode()的值进行hash计算,根据hash值得到这个元素在数组中的位置,将元素存储在该位置的链表上。
当我们使用get的时候,首先HashMap会对key的hashCode()的值进行hash计算,根据hash值得到这个元素在数组中的位置,将元素从该位置上的链表中取出


当多线程的情况下,可能产生条件竞争。当重新调整HashMap大小的时候,确实存在条件竞争,如果两个线程都发现HashMap需要重新调整大小了,
它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的数组位置的时候,
HashMap并不会将元素放在LinkedList的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了

ConcurrentHashMap基于双数组和链表的Map接口的同步实现 
ConcurrentHashMap中元素的key是唯一的、value值可重复 
ConcurrentHashMap不允许使用null值和null键 
ConcurrentHashMap是无序的


为什么使用ConcurrentHashMap:
我们都知道HashMap是非线程安全的,当我们只有一个线程在使用HashMap的时候,自然不会有问题,但如果涉及到多个线程,并且有读有写的过程中,HashMap就会fail-fast。要解决HashMap同步的问题,我们的解决方案有:Hashtable 、Collections.synchronizedMap(hashMap) 
这两种方式基本都是对整个hash表结构加上同步锁,这样在锁表的期间,别的线程就需要等待了,无疑性能不高,所以我们引入ConcurrentHashMap,既能同步又能多线程访问
ConcurrentHashMap的数据结构:
ConcurrentHashMap的数据结构为一个Segment数组,Segment的数据结构为HashEntry的数组,而HashEntry存的是我们的键值对,可以构成链表。可以简单的理解为数组里装的是HashMap

3)如何实现HashMap顺序存储:可以参考LinkedHashMap的底层实现
LinkedHashMap底层使用哈希表与双向链表来保存所有元素,它维护着一个运行于所有条目的双向链表(如果学过双向链表的同学会更好的理解它的源代码),此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序 
1.按插入顺序的链表:在LinkedHashMap调用get方法后,输出
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值