当前位置 博文首页 > m0_46212244的博客:java集合面试题

    m0_46212244的博客:java集合面试题

    作者:[db:作者] 时间:2021-09-23 13:27

    Java集合方面的面试题

    1.集合里常用的类有哪些?

    (1)List、Set、Map、Queue
    

    2.Collection 和 Collections 有什么区别?

    (1)Collection是一个集合接口,它提供了集合对象进行基本操作的通用方法,所有集合都是它的子类,比如 List、Set 等;
    
    (2)Collections 是一个集合包装类,包含了很多操作集合的静态方法,不能被实例化,就像一个工具类,
    比如提供的排序方法:Collections. sort(list);
    

    3.List、Set、Map 之间的区别是什么?

    (1)list集合有序可重复,set无序不可重复,Map以键值对的形式对元素进行存储。Map不允许有重复键,
    但允许有不同键对应的重复的值。
    
    (2)List允许任意数量的空值,Set最多允许一个空值的出现,Map只允许出现一个空键,
    但允许出现任意数量的空值
    

    4.HashMap 和 Hashtable 有什么区别?

    (1)hashMap不是线程安全的,hashtable是线程安全的。
    
    (2)继承的父类不同,HashTable是继承自Dictionary类,而HashMap是继承自AbstractMap类。
    不过它们都同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
    
    (3)HashTable不允许存null值(key和value都不可以),HashMap允许null值(key和value)都可以。
    
    (4)HashTable使用Enumeration遍历,HashMap使用Iterator进行遍历。
    
    (5)Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。
    之后每次扩充,容量变为原来的2倍。
    
    (6)创建时,如果给定了容量初始值,那么Hashtable会直接使用你给定的大小,而HashMap会将其扩充
    为2的幂次方大小。也就是说Hashtable会尽量使用素数、奇数。而HashMap则总是使用2的幂作为哈希表的大小。
    
    (7)hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
    

    5.如何决定使用 HashMap 还是 TreeMap?

    (1)TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的
    ,TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。
    
    (2)HashMap<K,V>的Key值实现散列hashCode(),分布是散列的、均匀的,不支持排序;数据结构主要是数组,
    链表或红黑树。适用于在Map中插入、删除和定位元素。
    
    (3)结论:如果你需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。
    除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap。
    

    6.说一下 HashMap 的实现原理?

    (1)当我们往HashMap中put元素时,底层调用k的hashcode方法得出hash值,然后通过哈希算法将hash值转换为数组的下标,
    当下标上没有任何元素时,就把这个节点放在这个位置上。如果下标对应的位置上有链表,此时会拿着k和链表上的k进行equals比较,
    如果和所有链表上的k 进行equals返回都是false,则将这个节点加入到末尾。如果其中有一个返回为true。则将其的value值覆盖。
    如果添加时发现容量不够,就开始扩容
    
    (2)题外补充:hashMap的get(k)原理:
    	①先调用k的hashcode方法得出hash值,通过哈希算法将其转换为数组下标。通过数组下标快速定位,
    	如果该位置上什么都没有,则返回null,如果改位置上有链表,那么拿着参数k和链表上的所有k进行equals,
    	如果所有equals都返回false,则返回null。但是只要其中一个节点的k返回为true,get方法就将其value返回。
    	
    (3)题外补充之HashMap扩容机制(JDK8)
    	①首相说明一下,HashMap的底层数据结构是数组+链表-->红黑树。在第一次添加数据时,
    	默认构造函数构建初始容量为16,到达它的临界值时12(16 * 0.75=12),数组就会扩容致32(16 * 2).
    	然后新的临界值就是24(32 * 0.75)....依此类推,但是在JDK8之后,如果一条链表的元素个数到达8
    	且数组的大小到达64的时候,就会进化成红黑树
    

    7.说一下 HashSet 的实现原理?

    (1)HashSet底层由HashMap实现,扩容机制一样。它封装了一个HashMap来存储所有的集合元素。但是它不一样的是,
    它的所有集合元素由HashMap的key来保存,而HashMap的value则存储了一个PRESENT。它是一个静态的 Object 对象。
    
    (2)HashSet的其它操作原理都是基于HashMap的
    

    8.ArrayList 和 LinkedList 的区别是什么?

    (1)ArrayList底层采用动态数组的数据结构,LinkedList是链表的数据结构。
    
    (2)ArrayList查询快,插入和删除慢。LinkedList查询慢,插入、删除快
    
    (3)随机访问效率不同
    

    9.如何实现数组和 List 之间的转换?

    (1)List转换成为数组:调用ArrayList的toArray方法。
    

    在这里插入图片描述

    (2)数组转换成为List:调用Arrays的asList方法。
    

    在这里插入图片描述

    10.ArrayList 和 Vector 的区别是什么?

    (1)Vector是同步的,而ArrayList不是,即ArrayList不是线程安全的。Vector是线程安全的
    
    (2)ArrayList和Vector都会根据实际的需要动态的调整容量,只不过在Vector扩容每次会增加1倍,而ArrayList只会增加50%
    
    (3)补充:如果只有一个线程去访问集合那么使用ArrayList,他不考虑线程安全的问题,
    所以效率会高一些如果是多个线程去访问集合,那么使用Vector
    

    11.Array 和 ArrayList 有何区别?

    (1)Array 数组可以包含基本类型和对象类型,ArrayList 却只能包含对象类型。
    Array 数组在存放的时候一定是同种类型的元素。ArrayList 就不一定了 。
    
    (2)Array 数组的空间大小是固定的,所以需要事前确定合适的空间大小。
    ArrayList 的空间是动态增长的,而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。
    
    (3)ArrayList 方法上比 Array 更多样化,比如添加全部 addAll()、删除全部 
    removeAll()、返回迭代器 iterator() 等
    

    12.在 Queue 中 poll()和 remove()有什么区别?

    (1)相同点:两者都是打印第一个元素并删除第一个元素
    
    (2)不同点:如果没有元素 remove()会直接抛NoSuchElementException 异常,而 poll()会返回 null。
    

    13.哪些集合类是线程安全的?

    (1)vector 就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用
    
    (2)statck:堆栈类,先进后出
    
    (3)hashtable:就比hashmap多了个线程安全。
    
    (4)enumeration:枚举,相当于迭代器。
    

    14.迭代器 Iterator 是什么?

    (1)迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,但是该对象比较特殊,
    不能直接创建对象,该对象是以内部类的形式存在于每个集合类的内部。迭代器通常被称为“轻量级”对象,
    因为创建它的代价小。
    

    15.Iterator 怎么使用?有什么特点?

    (1)使用:
    	①iterator只能单向移动:
    	②使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,
    	它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
    	③使用next()获得序列中的下一个元素。
    	④使用hasNext()检查序列中是否还有元素。
    	⑤使用remove()将迭代器新返回的元素删除。
    	
    (2)特点:
    	①terator在遍历元素过程中,有线程修改集合元素会有ConcurrentModificationEception异常
    

    16.Iterator 和 ListIterator 的区别?

    (1)Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List,而Set并不能使用ListIterator
    
    (2)ListIterator有add()方法,可以向List中添加对象,而Iterator不能。
    
    (3)ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历。但是ListIterator有hasPrevious()和previous()方法,
    可以实现逆向(顺序向前)遍历。Iterator就不可以。
    
    (4)ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator 没有此功能。
    
    (5)都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,
    不能修改。因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。
    

    17.集合和数组的区别?

    (1)数组是固定长度的;集合可变长度的。
    
    (2)数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
    
    (3)数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
    

    18.怎么确保一个集合不能被修改?

    (1)可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,
    这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。
    

    19.如何边遍历边移除 Collection 中的元素?

    (1)边遍历边修改 Collection 的唯一正确方式是使用 Iterator.remove() 方法
    

    20.遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么?

    (1)for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到最后一个元素后停止。
    
    (2)迭代器遍历,Iterator。Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,
    统一遍历集合的接口。Java 在 Collections 中支持了 Iterator 模式。
    
    (3)foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明 Iterator 或计数器。
    优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换。
    
    (4)最佳实践:Java Collections 框架中提供了一个 RandomAccess 接口,用来标记 List 实现是否支持 Random Access。
    如果一个数据集合实现了该接口,就意味着它支持 Random Access,按位置读取元素的平均时间复杂度为 O(1),如ArrayList。
    如果没有实现该接口,表示不支持 Random Access,如LinkedList。
    
    (5)推荐的做法就是:支持 Random Access 的列表可用 for 循环遍历,否则建议用 Iterator 或 foreach 遍历。
    

    21.说一下 ArrayList 的优缺点?

    (1)优点:
    	①ArrayList 底层以数组实现,是一种随机访问模式。ArrayList 实现了 RandomAccess 接口,因此查找的时候非常快。
    	②ArrayList 在顺序添加一个元素的时候非常方便。
    	
    (2)缺点:
    	①删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。
    	②插入元素的时候,也需要做一次元素复制操作,缺点同上。
    (3)结论:ArrayList 比较适合顺序添加、随机访问的场景。
    

    22.多线程场景下如何使用 ArrayList?

    (1)ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList
     方法将其转换成线程安全的容器后再使用。
    

    在这里插入图片描述
    23.为什么 ArrayList 的 elementData 加上 transient 修饰?
    (1)ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。
    transient 的作用是说不希望 elementData 数组被序列化.重写了 writeObject 实现:
    每次序列化时,先调用 defaultWriteObject() 方法序列化 ArrayList 中的非 transient 元素,
    然后遍历 elementData,只序列化已存入的元素,这样既加快了序列化的速度,
    又减小了序列化之后的文件大小。

    24.List 和 Set 的区别

    (1)List , Set 都是继承自Collection 接口
    
    (2)他们的特点:
    	①List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
    	②Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。
    	
    (3)他们的区别:
    	①List 支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。
    	②Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
    	③List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变
    

    25.HashSet如何检查重复?HashSet是如何保证数据不可重复的?

    (1)检查重复
    	①向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。
    	HashSet 中的add ()方法会使用HashMap 的put()方法。
    	
    (2)保证数据不重复
    	①HashMap 的 key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap 的key,
    	并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回新的V。
    	所以不会重复( HashMap 比较key是否相等是先比较hashcode 再比较equals )。
    

    26.HashSet与HashMap的区别?

    (1)它们的实现接口不一样,后者实现了Map接口,前者实现Set接口
    
    (2)HshMap存储键值对,HashSet存储对象
    
    (3)HashMap使用put添加元素值Map,HshSet使用add方法
    
    (4)计算hashCode值不一样。后者使用键对象计算。前者使用成员对象来计算
    
    (5)HashMap比HashSet的效率快
    

    27.为什么HashMap中String、Integer这样的包装类适合作为K?

    (1)String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率
    
    (2)都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况
    
    (3)内部已重写了equals()、hashCode()等方法,遵守了HashMap内部的规范,不容易出现Hash值计算错误的情况;
    

    28.如果使用Object作为HashMap的Key,应该怎么办呢?

    (1)重写hashCode和equals方法
    

    29.HashSet和TreeSet分别如何实现去重的?

    (1)HashSet:添加元素时,先得到hash值,会转成索引值,找到存储数据表table,看这个索引位置是否有元素,
    如果没有,则直接加入。如果有元素,即调用equals比较(记得要重写equals),如果相同,就放弃添加,
    如果不相同,就将其放到最后。如果添加时发现容量不够,开始扩容
    
    (2)TreeSet去重机制:如果你传入的一个Comparator匿名对象,就使用实现的comparator去重,
    如果方法返回0,就认为是相同的元素,就不添加,如果你没有传入一个Comparator匿名对象,
    则以你添加的对象实现的Comparable接口里的compareTo方法去重
    

    29.创建一个User类,TreeSet treeSet = new TreeSet();treeSet.add(new User);会不会报错?为什么?

    (1)首先会不会报错取决于你的这个User类有没有实现Comparable接口,如果没有去实现这个接口,
    那么在TreeSet使用add方法的时候,它的底层会去将User类转成Comparable类型。这时候,
    你没实现这个接口,那么会报出一个类型异常的错误。
    

    30.说说你对红黑树的见解?

    (1)每个节点非红即黑
    
    (2)根节点总是黑色的
    
    (3)如果节点是红色的,则它的子节点必须是黑色的(反之不一定)
    
    (4)每个叶子节点都是黑色的空节点(NIL节点)
    
    (5)从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)
    
    cs