Java容器面试题

1、set(HashSet、LinkedHashSet、TreeSet)

Set不包含重复的元素,这是Set最大的特点,也是使用Set最主要的原因。常用到的Set实现有 HashSetLinkedHashSetTreeSet

Collection

  • HashSet:委托给HashMap进行实现,实现了Set接口;
      HashSet是采用hash表来实现的,其中的元素没有按顺序排列,add()、remove()以及contains()等方法都是复杂度为O(1)的方法;
  • LinkedHashSet:是HashSet的子类,被委托给HashMap的子类LinkedHashMap进行实现,实现了Set接口;

LinkedHashSet继承于HashSet,利用下面的HashSet构造函数即可,注意到,其为包访问权限,专门供LinkedHashSet的构造函数调用。LinkedHashSet性能介于HashSet和TreeSet之间,是HashSet的子类,也是一个hash表,但是同时维护了一个双链表来记录插入的顺序,基本方法的复杂度为O(1)。

  • TreeSet:委托给TreeMap(TreeMap实现了NavigableSet接口)进行实现,实现了NavigableSet接口(扩展的 SortedSet);

TreeSet是采用树结构实现(红黑树算法),元素是按顺序进行排列,但是add()、remove()以及contains()等方法都是复杂度为O(log (n))的方法,它还提供了一些方法来处理排序的set,如first()、 last()、 headSet()和 tailSet()等。此外,TreeSet不同于HashSetLinkedHashSet,其所存储的元素必须是可排序的(元素实现Comparable接口或者传入Comparator),并且不能存放null值。

2、Map及Map的三种常用实现


HashMap

1、对NULL值处理
HashMap可以保存键为NULL的键值对,且该键值对是唯一的,若再次向其中添加键为NULL的键值对,将覆盖其原值。此外,如果HashMap中存在键为NULL的键值对,那么一定在第一个桶中;

2、HashMap 中的哈希策略

  • 使用 hash() 方法用于对Key的hashCode进行重新计算,以防止质量低下的hashCode()函数实现。由于hashMap的支撑数组长度总是 2 的倍数,通过右移可以使低位的数据尽量的不同,从而使Key的hash值的分布尽量均匀;
  • 使用 indexFor() 方法进行取余运算,以使Entry对象的插入位置尽量分布均匀;

3、HashMap 的底层数组长度为何总是2的n次方?

  • 构造函数及扩容策略
// HashMap 的容量必须是2的幂次方,超过 initialCapacity 的最小 2^n 
int capacity = 1;
while (capacity < initialCapacity)
    capacity <<= 1; 
  • 当底层数组的length为2的n次方时,h&(length - 1) 就相当于对length取模,而且速度比直接取模快得多,这是HashMap在速度上的一个优化;

4、初始容量16,两倍扩容,先添加再检查


LinkedHashMap:HashMap + 带头结点的双向循环链表
LinkedHashMap
1、可以实现LRU算法;
2、维持插入顺序或者维持访问顺序:双向循环链表头结点header + accessOrder;
3、利用双向循环链表重写迭代器;


TreeMap:红黑树的实现

排序二叉树虽然可以快速检索,但在最坏的情况下:如果插入的节点集本身就是有序的,要么是由小到大排列,要么是由大到小排列,那么最后得到的排序二叉树将变成链表:所有节点只有左节点(如果插入节点集本身是大到小排列);或所有节点只有右节点(如果插入节点集本身是小到大排列)。在这种情况下,排序二叉树就变成了普通链表,其检索效率就会很差;

红黑树:
  性质 1:每个节点要么是红色,要么是黑色;
  性质 2:根节点永远是黑色的;
  性质 3:所有的叶节点都是空节点(即 null),并且是黑色的;
  性质 4:每个红色节点的两个子节点都是黑色(从每个叶子到根的路径上不会有两个连续的红色节点);
  性质 5:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。

红黑树
红黑树的查找、插入与删除操作
红黑树上的只读操作与普通排序二叉树上的只读操作完全相同,只是红黑树保持了大致平衡,因此检索性能比排序二叉树要好很多。 但在红黑树上进行插入操作和删除操作会导致树不再符合红黑树的特征,因此插入操作和删除操作都需要进行一定的维护,以保证插入节点、删除节点后的树依然是红黑树。

3、Collection.sort()原理

Collections.sort 排序通过泛化实现对所有类型的排序,对于基础类型,如int,string按照字符表,数字大小排序,对于自定义类型,通过实现Comparable接口,重写compareto函数自定义比较大小的方式,接受对象类型 extends Comparable<? super T>或者Comparator外比较器,Comparable接口的方式比实现Comparator接口耦合性要强;

Collections.sort 内部内部调用的是Arrays.sort方法,对于Arrays类有两个sort方法,一个是sort(object),一个是sort(int),前者用的是归并排序,后者是快速排序;

4、CAS实现原理,以及ABA问题

一个CAS方法包含三个参数CAS(V,E,N)。V表示要更新的变量,E表示预期的值,N表示新值。只有当V的值等于E时,才会将V的值修改为N。如果V的值不等于E,说明已经被其他线程修改了,当前线程可以放弃此操作,也可以再次尝试次操作直至修改成功。基于这样的算法,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰(临界区值的修改),并进行恰当的处理。

由于 CAS 设计机制就是获取某两个时刻(初始预期值和当前内存值)变量值,并进行比较更新,所以说如果在获取初始预期值和当前内存值这段时间间隔内,变量值由 A 变为 B 再变为 A,那么对于 CAS 来说是不可感知的,但实际上变量已经发生了变化;解决办法是在每次获取时加版本号,并且每次更新对版本号 +1,这样当发生 ABA 问题时通过版本号可以得知变量被改动过。JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值