JAVA面试题大全(二)

1、java 容器都有哪些?

Java中的容器主要可以分为两大类:Collection和Map。

Collection

  1. List:有序的集合,允许重复元素,可以通过索引访问元素。常用的实现类包括:

    • ArrayList:实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
    • LinkedList:链表实现,提供了在列表的开头和结尾进行操作的便利方法。
    • Vector:和ArrayList类似,但是是同步的,因此是线程安全的。但同步需要花费机器时间,所以Vector的执行效率要低于ArrayList。
    • Stack:栈是Vector的一个子类,它实现了一个后进先出(LIFO)的堆栈。
  2. Set:不允许重复元素的集合,不保证元素的顺序。常用的实现类包括:

    • HashSet:基于哈希表的实现,提供了高效的添加、删除和查找操作。
    • TreeSet:基于红黑树的实现,提供了自然排序或自定义排序的功能。
    • LinkedHashSet:哈希表和链表实现的结合,可以维护元素的插入顺序。

Map

Map提供key到value的映射,一个Map中不能包含相同的key,每个key只能映射一个value。常用的实现类包括:

  1. HashMap:基于哈希表的实现,提供了高效的添加、删除和查找操作。
  2. Hashtable:和HashMap类似,但是是同步的,因此是线程安全的。但同步需要花费机器时间,所以Hashtable的执行效率要低于HashMap。
  3. TreeMap:基于红黑树的实现,提供了自然排序或自定义排序的功能。
  4. LinkedHashMap:哈希表和链表实现的结合,可以维护元素的插入顺序。
  5. ConcurrentHashMap:适用于多线程环境的哈希表实现,提供了高效的并发性能。

此外,Java还提供了其他的容器类,如ArrayDeque(双端队列)、PriorityQueue(优先队列)等,它们分别提供了不同的功能和性能特点。

2、Collection 和 Collections 有什么区别?

Collection是集合的接口,其实现类有List和Set;

Collections是工具类,包含许多有关集合操作的静态多态方法,可以直接使用

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

List、Set和Map在Java中都是集合框架(Collections Framework)的重要部分,但它们之间有明显的区别:

  1. List(列表)

    • 有序性:List中的元素按照它们被添加的顺序进行存储,并且可以通过索引访问每个元素。
    • 可重复性:List允许存储相同的元素多次。也就是说,可以在List中添加重复的元素,并且它们可以保持各自的位置和顺序。
    • 常见的实现类:ArrayList(动态数组实现,适用于查找和随机访问操作)和LinkedList(链表实现,提供了在列表的开头和结尾进行操作的便利方法)。
  2. Set(集合)

    • 无序性:Set集合中的元素是无序的,不能通过下标或者位置来访问元素。遍历Set集合时获取元素的顺序是不确定的。
    • 元素唯一性:Set集合中的元素是唯一的,不会存在重复的元素。当试图添加重复元素时,新元素不会被添加进集合中。
    • 可变性:Set集合的元素可以被增加、删除或者修改,因此是可变的。
    • 哈希表实现:Java中的HashSet是通过哈希表来实现的,因此Set集合的插入、查询和删除操作都具有较快的速度。
    • 线程不安全:Set集合不是线程安全的,如果需要在多线程环境下使用,需要进行同步处理。
    • 支持数学中的集合运算:Set集合支持并集、交集、差集等运算,方便我们进行集合的操作。
  3. Map(映射)

    • 键值对存储:Map中存储的元素是以键值对的形式保存的,每个键值对包含一个键对象和一个值对象,可以根据键对象获取对应的值对象。
    • 键的唯一性:在Map中,每个键对象是唯一的,不能存在相同的键对象,如果向Map中添加一个已经存在的键对象,则会替换掉原有的值对象。
    • 支持null键和null值:HashMap和Hashtable支持null键和null值,TreeMap和ConcurrentHashMap不允许有null键。
    • 无序性:HashMap和Hashtable等散列表实现的Map在存储键值对时并不是以顺序方式存储的,因此不能保证元素的顺序。但如果使用LinkedHashMap,则可以按照插入顺序或访问顺序进行遍历。
    • 高效性:访问和修改Map集合中的元素都非常高效,可以通过哈希表实现,时间复杂度为O(1)。
    • 可以存储不同类型的键值对:Map的键和值可以是任何类型的对象,只要它们能够被正确地哈希和比较。

总的来说,List、Set和Map在Java集合框架中各有其特定的用途和特点。List用于存储有序且可重复的元素,Set用于存储无序且唯一的元素,而Map则用于存储键值对形式的元素,其中键是唯一的。

4、HashMap 和 Hashtable 有什么区别?

HashMap不是线程安全的,HashTable是线程安全的

HashMap允许Null Key和Null Value,HashTable不允许

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

  1. 性能
    • HashMap通常具有更高的性能,因为它的查找、插入和删除操作的时间复杂度都是O(1)。它通过哈希函数直接定位到数组的某个位置。
    • TreeMap的查找、插入和删除操作的时间复杂度是O(log n),因为它基于红黑树实现。在数据量较大时,HashMap的性能优势会更加明显。
  2. 排序
    • HashMap是无序的,它的存储和遍历顺序与元素的插入顺序无关。它只保证在单线程环境下遍历结果的一致性。
    • TreeMap则是有序的,它可以根据键的自然顺序或自定义的Comparator顺序进行排序和遍历。这使得TreeMap在需要按照一定顺序处理数据的情况下更加适用。
  3. 内存占用
    • TreeMap由于内部采用红黑树数据结构,因此它的内存占用相对较高,比HashMap稍微大一些。
  4. 线程安全性
    • HashMap和TreeMap都是非线程安全的。如果你在多线程环境下使用,需要额外的同步处理。但Java也提供了线程安全的ConcurrentHashMap作为HashMap的替代品。
  5. 具体需求
    • 如果你的应用需要高性能的键值对存储,且不关心元素的顺序,那么HashMap是更好的选择。
    • 如果你的应用需要按照键的排序顺序存储和遍历键值对,那么TreeMap是更好的选择。
  6. 其他特性
    • TreeMap还提供了诸如floorKey()ceilingKey()lowerKey()higherKey()等方法,这些方法可以根据给定的键找到最接近的键,这在某些场景下可能很有用。

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

HashMap 的实现原理主要基于数组、链表和红黑树。以下是其具体的实现原理:

  1. 初始化:HashMap 创建一个初始容量为 16 的数组,称为“哈希桶”。
  2. 哈希函数:当添加一个键值对时,HashMap 使用哈希函数来计算该键的哈希码。这个哈希码用来决定该键值对在数组中的索引位置。
  3. 存储键值对:
    • 如果该位置上没有其它键值对,则直接将该键值对存储在该位置上。
    • 如果该位置上已经存在一个或多个键值对(发生了哈希冲突),则遍历链表或树,查找是否已经存在相同的键。
      • 如果存在,则更新对应的值。
      • 如果不存在,则将该键值对添加到链表或树的末尾。
  4. 数据结构调整:
    • 如果链表长度超过了 8,则将链表转化为红黑树,以提高查找性能。
    • 如果树的节点数量少于 6,则将树转换回链表,以节省内存。
  5. 扩容:当数组的使用量达到了负载因子(默认为 0.75)时,HashMap 会自动进行扩容。扩容操作会将数组长度加倍,并重新计算所有键值对的哈希码,然后将它们放入新的哈希桶中。

在 Java 的不同版本中,HashMap 的扩容机制可能有所不同。例如,在 JDK 1.8 中,HashMap 在第一次调用 put 方法时才会初始化数组,并且在扩容过程中会正序遍历原来的数组,并保持链表中节点的相对顺序不变。

总的来说,HashMap 通过哈希函数和哈希桶实现了快速定位元素位置的功能,并通过链表和红黑树解决了哈希冲突的问题。同时,它还具有较好的扩展性和灵活性,可以根据需要自动进行扩容和调整数据结构。

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

HashSet是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet的操作相对比较简单,相关HashSet的操作,基本上都是直接调用底层的HashMap的相关方法来完成,HashSet不允许有重复的值,并且元素是无序的

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

ArrayList的数据结构是动态数组;LinkedList的数据结构是双向链表。

ArrayList比LinkedList在随机访问的时候效率要高,因为LinkedList是线性的数据结构,需要依次往后查找

在非首尾的增删操作,LinkedList要比ArrayList的效率要高,因为ArrayList在操作增删时要影响其他元素的下标

总结:需要频繁读取集合中的元素时,推荐使用ArrayList;插入和删除操作较多时,推荐使用LinkedList

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

在Java中,数组(Array)和List之间的转换是常见的操作。

数组转List

你可以使用Arrays.asList()方法将数组转换为List。但是要注意,Arrays.asList()返回的是一个固定大小的列表,不支持addremove操作(会抛出UnsupportedOperationException),因为它返回的实际上是一个Arrays的内部类,这个内部类实现了List接口但没有实现可变大小的方法。

 如果你需要一个可以修改的List,你需要将返回的List转换为一个新的ArrayList或其他可修改的List实现。

int[] intArray = {1, 2, 3, 4, 5};  
List<Integer> intList = Arrays.stream(intArray).boxed().collect(Collectors.toList());  
// 或者对于对象数组  
String[] strArray = {"a", "b", "c"};  
List<String> strList = Arrays.asList(strArray);  
  
// 如果需要修改List,可以转换为ArrayList  
List<String> modifiableStrList = new ArrayList<>(Arrays.asList(strArray));

List转数组

对于List转数组,你可以使用List.toArray()方法,但这个方法默认返回的是Object数组,所以你可能需要指定一个类型参数或者使用toArray(T[] a)方法。

List<String> strList = Arrays.asList("a", "b", "c");  
// 使用无参toArray方法,需要强制类型转换  
String[] strArray1 = (String[]) strList.toArray();  
  
// 使用带类型参数的toArray方法  
String[] strArray2 = strList.toArray(new String[0]);  
// 或者指定数组大小(如果知道大小的话),但通常不需要  
// String[] strArray3 = strList.toArray(new String[strList.size()]);

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

相同点:都实现了List接口,都是有序集合

区别:Vector是线程安全的,ArrayList不是线程安全的;

当Vector或ArrayList中的元素超过它的初始大小时,Vector会将容量翻倍,而ArrayList只会将容量扩大50%

11、Array 和 ArrayList 有何区别?

Array类型的变量在声明时必须实例化;ArrayList可以只是先声明;

Array大小是固定的,而ArrayList的大小是动态变化的;

Array可以包含基本类型和对象类型,ArrayList只能包含对象类型

12、怎么确保一个集合不能被修改?

可以使用Collections.unmodifiableCollection(Collection c) 方法创建一个只读集合

这样改变集合的任何操作都会抛出Java. lang. UnsupportedOperationException异常。

13、Iterator 和 ListIterator 有什么区别?

ListIterator有add()方法,可以向List中添加对象,而Iterator不能;

ListIterator有hasPrevious()和previous()方法,可以向前遍历,Iterator不能;

ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现;

ListIterator可以实现对对象的修改,使用set()实现;而Iterator只能遍历,不能修改

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值