Java面试题03

1.Java容器都有哪些

        Java提供了丰富的容器类,包括Collection接口的实现类(如List、Set等)和Map接口的实现类(如HashMap、TreeMap等),它们分别用于存储不同类型的元素和键值对。

Java容器主要分为两种类型:Java集合容器和Java Web容器。

1.1 Java集合容器

Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有三种子类型,List、Set 和 Queue,实现了这些接口的容器类可以用来存储数据。

1.1.1 List(如 ArrayList、LinkedList):List 是有序集合,会按照元素插入的顺序保存元素。并且它允许包含重复元素。
1.1.2 Set(如 HashSet、TreeSet):Set 是一种不包含重复的元素的集合。它最多包含一个 null 元素。
1.1.3 Queue(如 PriorityQueue、ArrayDeque):Queue 是一种特殊的线性表,只允许在列表的前端(front)进行删除操作,而在列表的尾端(rear)进行插入操作。
1.1.4 Map(如 HashMap、TreeMap):Map 是一种对象,它存储的是键/值对。键和值都是对象。在 Map 中键是不能重复的,但是值可以。

1.2 Java Web容器

Java Web容器,或者说Servlet容器,是一种运行Web应用的地方,它为Web应用提供运行环境。目前流行的Java Web容器有:Tomcat、Jetty、GlassFish等。

2.Collection 和 Collections 有什么区别?

Collection是Java集合框架的根接口,定义了基本的集合操作方法,如添加、删除、遍历 等。Collections是一个包含静态方法的工具类,提供了一些集合操作的实用方法,如排序、查找 等

Collection 和 Collections 都是Java集合框架中的一部分,但是它们之间有一些重要的区别。

  1. 类型:Collection 是集合类的上层接口,它是一个单数形式的集合;而 Collections 是一个帮助类,它是集合框架的“外包装”,用于存储并处理集合。
  2. 功能:Collections 类包含一些对集合的排序、搜索以及序列化的操作。它是一个包装类,可以用来对集合进行各种操作,例如排序、搜索等。而 Collection 接口是Set接口和List接口的父接口,它定义了一些基本的操作,例如添加、删除元素等。

总结来说,Collection 和 Collections 的主要区别在于类型和功能。Collection 是集合的接口,定义了集合的基本操作;而 Collections 是帮助类,提供了对集合进行排序、搜索和序列化等操作的方法。

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

List是有序集合,允许存储重复元素,可以通过索引访问元素;Set是无序集合,不允许存储重复元素;Map是键值对的映射,每个键都唯一对应一个值。

List、Set、Map 是Java中常用的三种集合类型,它们之间有一些区别:

  1. 结构特点:List 和 Set 是存储单列数据的集合,Map 是存储键和值这样的双列数据的集合。
  2. 元素重复性:List允许有重复的元素;Set不允许元素重复;Map允许有重复键,但不允许有重复的键值对。
  3. 元素有序性:List及其所有实现类保持了每个元素的插入顺序;Set中的元素都是无序的;Map中的元素也是无序的。

4.HashMap 和 Hashtable 有什么区别?

HashMap是非线程安全的,允许有null键和值,效率较高;Hashtable是线程安全的,不允 许有null键和值,效率相对较低。

HashMap 和 Hashtable 都是Java中的Map接口的两种实现,它们之间有以下区别:

  1. 性能:,如果只是要获取一个键对应的值,无论是HashMap还是Hashtable,性能都是O(1)。但在插入或删除操作时,由于Hashtable需要创建新的键值对,而HashMap不需要(如果键已存在),因此在这方面,HashMap的性能可能会稍微优于Hashtable。
  2. 线程安全性:Hashtable 是线程安全的,而 HashMap 不是线程安全的。
  3. Null key and null value:HashMap允许使用null作为键和值,而Hashtable不允许使用null作为键或值。
  4. Size and iteration time:Hashtable和HashMap都使用哈希表(hash table)实现,但是Hashtable的size和迭代时间都是固定的,而HashMap的size和迭代时间是不固定的。

HashMap 和 Hashtable 都是 Java 中用于存储 Key-Value 对的集合,但是它们之间存在几个主要的区别:

  1. 线程安全性Hashtable 是线程安全的,它在每个公开的方法(例如 getputremove 等)上都有同步控制,这意味着在多线程环境下,你可以安全地使用 Hashtable。相反,HashMap 不是线程安全的,如果在多线程环境下使用,需要自己处理同步问题。
  2. 性能:因为 Hashtable 在每个方法上都有同步控制,所以它的性能会比 HashMap 低。在单线程环境下,HashMap 的性能通常会比 Hashtable 高得多。
  3. 允许的 key 值HashMap 允许使用 null 作为 key,而 Hashtable 不允许。
  4. 遍历Hashtable 继承自 Dictionary 类,因此可以使用 Enumeration 来遍历键和值。而 HashMap 继承自 AbstractMap 类,使用 Iterator 来遍历。
  5. 初始容量和负载因子HashMap 在创建时没有设定初始容量和负载因子,但这两个参数在 Hashtable 中是必需的。
  6. NullPointerException:Hashmap是允许key和value为null值的,用containsValue和containsKey方法判断是否包含对应键值对;HashTable键值对都不能为空,否则包空指针异常。
  7. 方法返回值:如果尝试获取不存在的 key 的值,HashMap 中的 get() 方法返回 null,而 Hashtable 返回 nullKey()
  8. 遍历顺序HashMap 不保证映射的顺序,特别是它不保证该顺序恒久不变。相比之下,Hashtable 的顺序是固定的,因为它的迭代器按照键的插入顺序进行迭代。

总的来说,如果你在多线程环境下工作并且需要一个线程安全的映射,那么应该使用 Hashtable。如果你在单线程环境下工作并且需要高性能,那么应该使用 HashMap

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

如果需要无序、高效的键值对存储,可使用HashMap;如果需要按键有序存储,可使用 TreeMap,它会根据键的自然顺序或指定的比较器进行排序

  1. 数据结构:HashMap 是基于哈希表实现的,而 TreeMap 是基于红黑树(一种自平衡二叉查找树)实现的。
  2. 性能:在单线程环境下,HashMap 的性能通常优于 TreeMap,因为 HashMap 的 get 和 put 操作的时间复杂度为 O(1),而 TreeMap 的 get 和 put 操作的时间复杂度为 O(log n)。但在多线程环境下,TreeMap 的性能可能更优,因为它支持对数据的并发访问。
  3. Null keys and null values:HashMap 允许使用 null 作为键(key)和值(value),而 TreeMap 不允许使用 null 作为键(key),但可以使用 null 作为值(value)。
  4. Order:TreeMap 中的元素是有序的,即按照键(key)的升序排序。因此,如果你需要有序的数据结构,应该选择 TreeMap。而 HashMap 不保证元素的顺序。
  5. Size:如果需要存储大量的数据并且需要快速查找,HashMap 可能是更好的选择,因为它的空间复杂度为 O(n),而 TreeMap 的空间复杂度为 O(n log n)。
  6. 线程安全:HashMap 不是线程安全的,而 TreeMap 是线程安全的。如果多个线程同时修改 TreeMap,它仍然保持一致性和线程安全。

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

HashMap基于哈希表实现,内部使用一个数组来存储Entry对象,通过键的哈希码确定数组 位置,当多个键映射到同一个位置时,使用链表或红黑树解决冲突。

HashMap 是 Java 集合框架中的重要组成部分,它基于哈希表(Hash table)实现。以下是 HashMap 的基本实现原理:

  1. 基本数据结构:HashMap 的内部主要由数组实现,这个数组的元素是链表或红黑树(从 Java 8 开始)的实例。链表用于解决哈希冲突,红黑树则用于当链表长度过长(超过一定阈值)时,将链表转化为红黑树以提高性能。
  2. 哈希函数:HashMap 使用哈希函数(hash function)将键(key)映射到数组的索引位置。哈希函数的设计对 HashMap 的性能至关重要。理想情况下,哈希函数应当将键均匀地映射到数组的各个位置,从而最大限度地减少哈希冲突。
  3. 哈希冲突:尽管我们希望哈希函数能将键均匀地映射到数组的各个位置,但实际情况中,不同的键可能会被映射到相同的位置,这就是所谓的哈希冲突。HashMap 使用链表或红黑树来解决哈希冲突。当两个或更多的键哈希到同一位置时,它们在链表或红黑树中相遇并解决冲突。
  4. 扩容与再哈希:当 HashMap 中的元素数量达到一定的阈值(默认为数组长度的 0.75 倍)时,HashMap 会进行扩容(rehashing)。扩容会导致所有元素重新计算哈希值并重新放置到新的数组中。这是一个比较耗时的操作,因此,合理设定初始容量可以减少扩容的频率,从而提高 HashMap 的性能。
  5. 遍历:HashMap 提供了遍历所有元素的方法,如 keySet() 和 values()。在遍历过程中,HashMap 会按照其内部数据结构的顺序返回元素,但这个顺序并不是按照插入顺序或者访问顺序,而是根据哈希值确定的。

以上就是 HashMap 的基本实现原理。需要注意的是,由于 HashMap 的内部实现细节可能会因 Java 版本的不同而有所差异,因此在实际使用中,应当根据具体情况选择合适的数据结构和参数设置。

7.说一下 HashSet 的实现原理

HashSet基于HashMap实现,它实际上是在HashMap的键部分存储元素,而值部分存储一 个常量。

  1. 数据结构:HashSet 底层实际上是一个 HashMap,它使用一个数组来存储元素,这个数组的每个元素都是一个链表。
  2. 元素存储:HashSet 中的元素都存储在 HashMap 的 key 上,而 value 则是一个固定的对象(private static final Object PRESENT = new Object())。这是因为 HashSet 不允许有重复元素,所以所有的元素都是通过唯一的 key 来标识的。
  3. 添加元素:当我们调用 add 方法时,实际上是调用了底层 HashMap 的 put 方法。如果 key 已经存在,put 方法会修改对应的 value;如果 key 不存在,put 方法会插入新的 key-value 对应关系。
  4. 迭代顺序:HashSet 不保证元素的迭代顺序,特别是不保证该顺序恒久不变。这是因为 HashMap 的实现中,链表元素的顺序并不是按照插入顺序或者访问顺序排列的,而是根据 key 的哈希值确定的。
  5. Null 元素:HashSet 允许使用 null 作为元素。

总的来说,HashSet 的实现充分利用了 HashMap 的特性,通过将元素作为 HashMap 的 key 来存储,并利用哈希表的高效查找特性来实现元素的快速添加和查找。同时,由于 HashMap 中 value 的设计,使得 HashSet 不需要担心重复元素的插入问题。

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

ArrayList是基于动态数组实现,通过索引访问元素速度快,插入和删除元素较慢; LinkedList是基于双向链表实现,插入和删除元素速度快,但访问元素较慢

ArrayList 和 LinkedList 是 Java 中两种常用的 List 类型,它们在内部实现方式和使用场景上存在一些重要的区别:

  1. 底层实现:ArrayList 是基于动态数组实现的,而 LinkedList 是基于双向链表实现的。
  2. 内存空间:对于 ArrayList,当元素数量超过数组默认的大小(例如,对于 int 类型,默认为10)时,ArrayList 会自动进行扩容,可能涉及到内存空间的连续申请和复制,因此对于大量数据的随机访问效率较高。对于 LinkedList,每个节点都会存储前一个和后一个节点的引用,所以内存空间利用率相对较低,但是它在插入和删除元素时的效率较高。
  3. 插入和删除:由于 ArrayList 基于数组实现,因此在列表的中间位置插入和删除元素效率较低,因为可能需要移动大量的元素。而 LinkedList 在列表的中间位置插入和删除元素效率较高,因为它只需更改一些节点的引用即可。
  4. 遍历:两者的遍历速度都很快,因为它们都支持 for-each 循环。但是,如果需要逆序遍历,LinkedList 的效率会更高。
  5. 容量增长:当 ArrayList 的元素数量超过其容量时,它通常会创建一个新的数组并复制原有的元素到新的数组中。这个过程可能会导致性能下降。LinkedList 则不需要这样做,因此在大规模数据操作时,LinkedList 可能更优。
  6. 线程安全性:两者都不是线程安全的。如果多个线程同时修改 ArrayList 或 LinkedList,可能会导致数据不一致。如果需要线程安全,可以考虑使用 CopyOnWriteArrayList 或者使用并发库中的 ConcurrentLinkedQueue。
  7. 应用场景:ArrayList 更适合查询操作(即查找、获取),因为其基于数组实现,随机访问效率高。LinkedList 更适合插入和删除操作,因为其基于链表实现,插入和删除效率高。

总的来说,选择 ArrayList 还是 LinkedList 取决于你的具体需求和使用场景。如果你需要频繁地访问元素,那么 ArrayList 可能是更好的选择。如果你需要频繁地插入和删除元素,那么 LinkedList 可能更适合你。

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

可以使用Arrays.asList()方法将数组转换为List,或者使用ArrayList的构造函数将List转换为 数组。

1. 数组转 List:

你可以使用 Arrays.asList() 方法将数组转换为 List。例如:

String[] array = {"a", "b", "c"};
List list = Arrays.asList(array);

需要注意的是,通过 Arrays.asList() 返回的 List 是一个固定大小的 List,它直接引用原数组,所以原数组改变时,这个 List 也会跟着改变。它并不是一个可改变的 List。

如果你需要一个可以修改的 List,可以使用 ArrayList 的构造方法:

String[] array = {"a", "b", "c"};
List list = new ArrayList<>(Arrays.asList(array));

这样得到的 List 就可以被修改,并且不会影响原数组。

2. List 转数组:

你可以使用 List.toArray() 方法将 List 转换为数组。例如:

List list = new ArrayList<>(Arrays.asList("a", "b", "c"));
String[] array = new String[list.size()];
list.toArray(array);

在这个例子中,我们首先创建了一个大小为 List 大小的数组,然后调用 List.toArray() 方法将 List 的元素填入数组。需要注意的是,这里数组的大小必须和 List 的大小一样,否则会抛出 ArrayStoreException

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

ArrayList 和 Vector 是 Java 中两种常用的动态数组实现,它们都实现了 List 接口,但在使用和特性上有一些区别:

  1. 同步性(Synchronization):这是 ArrayList 和 Vector 最主要的区别。Vector 是同步的,这意味着它是线程安全的,但这也意味着在单线程环境下,由于线程同步的开销,它的性能可能比非同步的 ArrayList 要低。而 ArrayList 是非同步的,它在单线程环境下可以提供更高的性能。
  2. 性能:由于同步性,ArrayList 在单线程环境下的性能通常优于 Vector。但是,如果你需要线程安全,并且性能不是关键因素,那么 Vector 也可以是一个选择。
  3. 初始化容量和增长:当创建 ArrayList 或 Vector 时,你可以指定初始容量。Vector 的初始容量是固定的,而 ArrayList 的初始容量可以改变。当 ArrayList 的元素数量超过其容量时,它会自动进行扩容。
  4. 使用场景:ArrayList 和 Vector 都可以用于存储和操作数组的元素,但它们的适用场景不完全相同。如果你需要一个可以快速、随机访问的列表,并且不需要考虑线程安全问题,那么 ArrayList 是一个更好的选择。如果你需要一个线程安全的列表,或者需要存储大量的数据并且希望内存使用效率更高,那么 Vector 可能更适合你。
  5. 遗留与现代:Vector 被认为是较旧的数据结构,现在较少使用。ArrayList 是一个更现代的选择,被认为更灵活、更强大、更易于使用。

你可能感兴趣的:(Java面试八股文,java,开发语言)