java面试题集中营-02集合

由于本人要准备面试,所以更新一些常见的,自己收集的面试题

java集合框架

List

Vector和ArrayList、LinkedList联系和区别?分别的使用场景

ArrayList是数组实现的,线程不安全,查询修改快,增加和删除慢
LinkList是双向链表实现的,线程不安全,查询修改满,增加和删除快
Vector: 底层是数组实现,线程安全的,操作的时候使用synchronized进行加锁

ArrayList是基于数组,数组在内存上是连续开辟的空间,数组中的每个元素的类型一致,占用的空间大小也就是一致。通过数组的下标,我们就能知道查询的元素的的偏移量,查询就快。而且CPU缓存会读入一段连续的内存,数组符合连续的内存,所以数组可以被缓存处理。正因为是连续的,删除一个元素或者增加一个元素,需要移动该元素的后续元素。数组扩容时要复制整个数组内容要扩大的数组中。所以增加和删除慢

LinkList是基于双向链表(每一个元素有两个指针,一个指向前一个元素,一个指向后一个元素,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点)。链表的存储结构是不连续的,靠着指针连在一起。每次查询都要从节点头部或者尾部来遍历查询数据。添加和删除元素,只需要操作指针,不需要元素移动,所以添加和删除快。

如果需要保证线程安全,ArrayList应该怎么做,用有几种方式
  • 自己写个包装类,根据业务一般是add/update/remove加锁

写个类继承ArrayList,重写他的add/update/remove,用lock和synchronized加锁

  • Collections.synchronizedList(new ArrayList<>()); 使用synchronized加锁

这个是把ArrayList中的操作全部加锁,源码如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5E0eJWqH-1598179364751)(https://imgkr2.cn-bj.ufileos.com/63017a51-ac8f-4168-93c6-66abe9981e8d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=1Ptksaf4p9a8zaChwfO6gzqcrB8%253D&Expires=1597300847)]
这个确定是所有操作都加锁,读取数据也加锁,这个会影响读取效率,不推荐

  • new CopyOnWriteArrayList<>() 使用ReentrantLock加锁

设置set源码:用了使用ReentrantLock加锁->复制了一个数组->修改数组的值->将原数组的引用地址改为新数组->解锁。这样会产生脏读,set元素没有完成,其他线程读取的是老数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S28l6rUY-1598179364753)(https://imgkr2.cn-bj.ufileos.com/6aa87a38-7160-4a99-bc86-58ddb6874949.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=FpkvStOQmCf2VfAdFAGKtKHVVKE%253D&Expires=1597301090)]

get源码:
get是没有加锁的,不影响读取效率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JUzL3mFH-1598179364753)(https://imgkr2.cn-bj.ufileos.com/3300dfbf-bffd-400e-9923-f7a3648cb528.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=Jr13wKLtghMfIYJXMEpMvZXur%252BU%253D&Expires=1597301405)]

了解CopyOnWriteArrayList吗?和 Collections.synchronizedList实现线程安全有什么区别, 使用场景是怎样的?
  • CopyOnWriteArrayList:执行修改操作时,会拷贝一份新的数组进行操作(add、set、remove等),代价十分昂贵,在执行完修改后将原来集合指向新的集合来完成修改操作,源码里面用ReentrantLock可重入锁来保证不会有多个线程同时拷贝一份数组

场景:读高性能,适用读操作远远大于写操作的场景中使用(读的时候是不需要加锁的,直接获取,删除和增加是需要加锁的, 读多写少)

  • Collections.synchronizedList:线程安全的原因是因为它几乎在每个方法中都使用了synchronized同步*锁

场景:写操作性能比CopyOnWriteArrayList好,读操作性能并不如CopyOnWriteArrayList

CopyOnWriteArrayList的设计思想是怎样的,有什么缺点?

设计思想:读写分离+最终一致
缺点:内存占用问题,写时复制机制,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象,如果对象大则容易发生Yong GC和Full GC。会产生脏读。

ArrayList的扩容机制是怎样的

JDK1.7之前ArrayList默认大小是10,JDk1.7之后是0

未指定集合容量,默认是0,若已经指定大小则集合大小为指定的;
当集合第一次添加元素的时候,集合大小扩容为10
ArrayList的元素个数大于其容量,扩容的大小= 原始大小+原始大小/2

Map

HashMap与Hashtable的区别
  • (底层不同)HashMap底层是基于数组+链表,Hashtable:基于哈希表实现,线程安全的(加了synchronized)
  • (同步性)HashTable的方法是同步的,HashMap不能同步。
  • (继承的父类不同)HashTable是继承自Dictionary类,而HashMap是继承自AbstractMap类。
  • (对null key和null value的支持不同).HashTable不允许null值(key和value都不可以),HashMap允许使用null值(key和value)都可以。这样的键只有一个,可以有一个或多个键所对应的值为null。\
介绍下对象的 hashCode()和equals(),使用场景
  • hashcode
    顶级类Object里面的方法,所有的类都是继承Object,返回是一个int类型的数
    根据一定的hash规则(存储地址,字段,长度等),映射成一个数组,即散列值
  • equals
    顶级类Object里面的方法,所有的类都是继承Object,返回是一个boolean类型
    根据自定义的匹配规则,用于匹配两个对象是否一样,一般逻辑如下

自定义实体类的时候,是可以重写这两个方法的。在实体类对象排序与对比的时候经常会用到

HashMap和TreeMap应该怎么选择,使用场景

hashMap:是基于数组+链表(java8之后引用了红黑树),线程不安全,key,value都能为空,但是key为空的元素最多只能有一个。
treeMap:是基于红黑树,可以自定义排序规则,每一个元素都要实现Comparator接口。线程不安全,key,null不能为空。主要用于排序。

如果需要线程安全,且效率高的Map,应该怎么做?

多线程环境下可以用concurrent包下的ConcurrentHashMap, 或者使用Collections.synchronizedMap()

ConcurrentHashMap效率高比hashtable高很多,原因就是hashtable所有的操作方法都是加锁。ConcurrentHashMap读操作不加锁的。

Collections.synchronizedMap返回的map是加锁的,但是也把读操作加锁,导致效率低下。

hashmap详解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YNw1jt6b-1598179364754)(https://imgkr2.cn-bj.ufileos.com/c6ffd435-08b5-4fff-9d57-591a6068b2fe.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=0GuJnOO%252FYVkUyprAi2ju0tpEplI%253D&Expires=1598265083)]

hashmap是基于数组+链表+红黑树(jdk8才有红黑树)。当一个key-value添加到hashmap时,会先计算key的hash值,按hash值存放在数组中。如果另外某一个数据存放数据时,发现对应数组的hash位置上有了数据,就会以链表的方式存放。如果链表的长度大于8,链表就会转为红黑树。

数组 Node<K,V>[] table ,根据对象的key的hash值进行在数组里面是哪个节点

链表的作用是解决hash冲突,将hash值一样的对象存在一个链表放在hash值对应的槽位

红黑树 JDK8使用红黑树来替代超过8个节点的链表,主要是查询性能的提升,从原来的O(n)到O(logn),
通过hash碰撞,让HashMap不断产生碰撞,那么相同的key的位置的链表就会不断增长,当对这个Hashmap的相应位置进行查询的时候,就会循环遍历这个超级大的链表,性能就会下降,所以改用红黑树。

为啥选择红黑树而不用其他树,比如二叉查找树,为啥不一直开始就用红黑树,而是到8的长度后才变换

二叉查找树在特殊情况下也会变成一条线性结构,和原先的链表存在一样的深度遍历问题,查找性能就会慢,
使用红黑树主要是提升查找数据的速度,红黑树是平衡二叉树的一种,插入新数据后会通过左旋,右旋、变色等操作来保持平衡,解决单链表查询深度的问题

数据量少的时候操作数据,遍历线性表比红黑树所消耗的资源少,且前期数据少 平衡二叉树保持平衡是需要消耗资源的,所以前期采用线性表,等到一定数之后变换到红黑树

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值