Java⾯试题总结-Java集合篇(附答案)
⽬录
⼀、Java 容器都有哪些?
1、Collection
(1)set
HashSet、TreeSet
(2)list
ArrayList、LinkedList、Vector
2、Map
HashMap、HashTable、TreeMap
⼆、Collection 和 Collections 有什么区别?
1、Collection是最基本的集合接⼝,Collection派⽣了两个⼦接⼝list和set,分别定义了两种不同的存储⽅式。
2、Collections是⼀个包装类,它包含各种有关集合操作的静态⽅法(对集合的搜索、排序、线程安全化等)。
此类不能实例化,就像⼀个⼯具类,服务于Collection框架。
三、list与Set区别
1、List简介
实际上有两种List:⼀种是基本的ArrayList,其优点在于随机访问元素,另⼀种是LinkedList,它并不是为快速随机访问设计的,⽽是快速的插⼊或删除。
ArrayList:由数组实现的List。允许对元素进⾏快速随机访问,但是向List中间插⼊与移除元素的速度很慢。
LinkedList :对顺序访问进⾏了优化,向List中间插⼊与删除的开销并不⼤。随机访问则相对较慢。
还具有下列⽅ 法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 这些⽅法 (没有在任何接⼝或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使⽤。
2、Set简介
Set具有与Collection完全⼀样的接⼝,因此没有任何额外的功能。实际上Set就是Collection,只是⾏为不同。这是继承与多态思想的典型应⽤:表现不同的⾏为。Set不保存重复的元素(⾄于如何判断元素相同则较为负责)
Set : 存⼊Set的每个元素都必须是唯⼀的,因为Set不保存重复元素。加⼊Set的元素必须定义equals()⽅法以确保对象的唯⼀性。Set与Collection有完全⼀样的接⼝。Set接⼝不保证维护元素的次序。
HashSet:为快速查设计的Set。存⼊HashSet的对象必须定义hashCode()。
TreeSet: 保存次序的Set, 底层为树结构。使⽤它可以从Set中提取有序的序列。
3、list与Set区别
(1)List,Set都是继承⾃Collection接⼝
(2)List特点:元素有放⼊顺序,元素可重复 ,Set特点:元素⽆放⼊顺序,元素不可重复,重复元素会覆盖掉,(元素虽然⽆放⼊顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加⼊Set 的Object必须定义equals()⽅法 ,另外list ⽀持for循环,也就是通过下标来遍历,也可以⽤迭代器,但是set只能⽤迭代,因为他⽆序,⽆法⽤下标来取得想要的值。)
(3)Set和List对⽐:
Set:检索元素效率低下,删除和插⼊效率⾼,插⼊和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查元素效率⾼,插⼊删除元素效率低,因为会引起其他元素位置改变。
四、HashMap 和 Hashtable 有什么区别?
1. HashMap是线程不安全的,HashTable是线程安全的;
2. HashMap中允许键和值为null,HashTable不允许;
3. HashMap的默认容器是16,为2倍扩容,HashTable默认是11,为2倍+1扩容;
五、说⼀下 HashMap 的实现原理?
1、简介
HashMap基于map接⼝,元素以键值对⽅式存储,允许有null值,HashMap是线程不安全的。
2、基本属性
1. 初始化⼤⼩,默认16,2倍扩容;
2. 负载因⼦0.75;
3. 初始化的默认数组;
4. size
5. threshold。判断是否需要调整hashmap容量
3、HashMap的存储结构
JDK1.7中采⽤数组+链表的存储形式。
HashMap采取Entry数组来存储key-value,每⼀个键值对组成了⼀个Entry实体,Entry类时机上是⼀个单向的链表结构,它具有next指针,指向下⼀个Entry实体,以此来解决Hash冲突的问题。
HashMap实现⼀个内部类Entry,重要的属性有hash、key、value、next。
JDK1.8中采⽤数据+链表+红⿊树的存储形式。当链表长度超过阈值(8)时,将链表转换为红⿊树。在性能上进⼀步得到提升。
六、set有哪些实现类?
1、HashSet
HashSet是set接⼝的实现类,set下⾯最主要的实现类就是HashSet(也就是⽤的最多的),此外还有LinkedHashSet和TreeSet。HashSet是⽆序的、不可重复的。通过对象的hashCode和equals⽅法保证对象的唯⼀性。
HashSet内部的存储结构是哈希表,是线程不安全的。
2、TreeSet
TreeSet对元素进⾏排序的⽅式:
元素⾃⾝具备⽐较功能,需要实现Comparable接⼝,并覆盖compareTo⽅法。
元素⾃⾝不具备⽐较功能,需要实现Comparator接⼝,并覆盖compare⽅法。
3、LinkedHashSet
LinkedHashSet是⼀种有序的Set集合,即其元素的存⼊和输出的顺序是相同的。
七、说⼀下 HashSet 的实现原理?
HashSet实际上是⼀个HashMap实例,数据存储结构都是数组+链表。
HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上⾯,⽽value都是⼀个统⼀的对象PRESENT。
private static final Object PRESENT = new Object();
HashSet中add⽅法调⽤的是底层HashMap中的put⽅法,put⽅法要判断插⼊值是否存在,⽽HashSet的add⽅法,⾸先判断元素是否存在,如果存在则插⼊,如果不存在则不插⼊,这样就保证了HashSet中不存在重复值。
通过对象的hashCode和equals⽅法保证对象的唯⼀性。
⼋、ArrayList 和 LinkedList 的区别是什么?
ArrayList是动态数组的数据结构实现,查和遍历的效率较⾼;
LinkedList 是双向链表的数据结构,增加和删除的效率较⾼;
九、如何实现数组和 List 之间的转换?
String[] arr = {"zs","ls","ww"};
List<String> list = Arrays.asList(arr);
System.out.println(list);
ArrayList<String> list1 = new ArrayList<String>();
list1.add("张三");
list1.add("李四");
list1.add("王五");
String[] arr1 = Array(new String[list1.size()]);
System.out.println(arr1);
for(int i = 0; i < arr1.length; i++){
System.out.println(arr1[i]);
}
⼗、在 Queue 中 poll()和 remove()有什么区别?
1、offer()和add()区别:
增加新项时,如果队列满了,add会抛出异常,offer返回false。
2、poll()和remove()区别:
poll()和remove()都是从队列中删除第⼀个元素,remove抛出异常,poll返回null。
3、peek()和element()区别:
peek()和element()⽤于查询队列头部元素,为空时element抛出异常,peek返回null。
⼗⼀、哪些集合类是线程安全的
1. Vector:就⽐Arraylist多了个同步化机制(线程安全)。
2. Stack:栈,也是线程安全的,继承于Vector。
3. Hashtable:就⽐Hashmap多了个线程安全。
4. ConcurrentHashMap:是⼀种⾼效但是线程安全的集合。
⼗⼆、迭代器 Iterator 是什么?
为了⽅便的处理集合中的元素,Java中出现了⼀个对象,该对象提供了⼀些⽅法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator)。
⼗三、Iterator 怎么使⽤?有什么特点?
Iterator 接⼝源码中的⽅法:
1. java.lang.Iterable 接⼝被 java.util.Collection 接⼝继承,java.util.Collection 接⼝的 iterator() ⽅法返回⼀个 Iterator 对象
2. next() ⽅法获得集合中的下⼀个元素
java面试题及答案20203. hasNext() 检查集合中是否还有元素
4. remove() ⽅法将迭代器新返回的元素删除
⼗四、Iterator 和 ListIterator 有什么区别?
1、ListIterator 继承 Iterator
2、ListIterator ⽐ Iterator多⽅法
add(E e) 将指定的元素插⼊列表,插⼊位置为迭代器当前位置之前
set(E e) 迭代器返回的最后⼀个元素替换参数e
hasPrevious() 迭代器当前位置,反向遍历集合是否含有元素
previous() 迭代器当前位置,反向遍历集合,下⼀个元素
previousIndex() 迭代器当前位置,反向遍历集合,返回下⼀个元素的下标
nextIndex() 迭代器当前位置,返回下⼀个元素的下标
3、使⽤范围不同,Iterator可以迭代所有集合;ListIterator 只能⽤于List及其⼦类
ListIterator 有 add ⽅法,可以向 List 中添加对象;Iterator 不能
ListIterator 有 hasPrevious() 和 previous() ⽅法,可以实现逆向遍历;Iterator不可以
ListIterator 有 nextIndex() 和previousIndex() ⽅法,可定位当前索引的位置;Iterator不可以
ListIterator 有 set()⽅法,可以实现对 List 的修改;Iterator 仅能遍历,不能修改。
⼗五、怎么确保⼀个集合不能被修改?
我们很容易想到⽤final关键字进⾏修饰,我们都知道
final关键字可以修饰类,⽅法,成员变量,final修饰的类不能被继承,final修饰的⽅法不能被重写,final修饰的成员变量必须初始化值,如果这个成员变量是基本数据类型,表⽰这个变量的值是不可改变的,如果说这个成员变量是引⽤类型,则表⽰这个引⽤的地址值是不能改变的,但是这个引⽤所指向的对象⾥⾯的内容还是可以改变的。
那么,我们怎么确保⼀个集合不能被修改?⾸先我们要清楚,集合(map,set,list…)都是引⽤类型,所以我们如果⽤final修饰的话,集合⾥⾯的内容还是可以修改的。
我们可以做⼀个实验:
可以看到:我们⽤final关键字定义了⼀个map集合,这时候我们往集合⾥⾯传值,第⼀个键值对1,1;我们再修改后,可以把键为1的值改为100,说明我们是可以修改map集合的值的。
那我们应该怎么做才能确保集合不被修改呢?
我们可以采⽤Collections包下的unmodifiableMap⽅法,通过这个⽅法返回的map,是不可以修改的。他会报
java.lang.UnsupportedOperationException错。
同理:Collections包也提供了对list和set集合的⽅法。
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)
⼗六、队列和栈是什么?有什么区别?
1、队列先进先出,栈先进后出。
2、遍历数据速度不同。
栈只能从头部取数据 也就最先放⼊的需要遍历整个栈最后才能取出来,⽽且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的⼀致性;
队列则不同,他基于地址指针进⾏遍历,⽽且可以从头或尾部开始遍历,但不能同时遍历,⽆需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多。
⼗七、Java8开始ConcurrentHashMap,为什么舍弃分段锁?
ConcurrentHashMap的原理是引⽤了内部的 Segment ( ReentrantLock ) 分段锁,保证在操作不同段 map 的时候, 可以并发执⾏,操作同段 map 的时候,进⾏锁的竞争和等待。从⽽达到线程安全, 且效率⼤于 synchronized。
但是在 Java 8 之后, JDK 却弃⽤了这个策略,重新使⽤了 synchronized+CAS。
弃⽤原因
通过 JDK 的源码和官⽅⽂档看来, 他们认为的弃⽤分段锁的原因由以下⼏点:
1. 加⼊多个分段锁浪费内存空间。
2. ⽣产环境中, map 在放⼊时竞争同⼀个锁的概率⾮常⼩,分段锁反⽽会造成更新等操作的长时间等待。
3. 为了提⾼ GC 的效率
新的同步⽅案
既然弃⽤了分段锁, 那么⼀定由新的线程安全⽅案, 我们来看看源码是怎么解决线程安全的呢?(源码保留了segment 代码, 但并没有使⽤)。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论