1 Star 0 Fork 31

阿明 / Ebooks

forked from Java精选 / Ebooks 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
常见Java集合面试题及答案汇总,2021年底最新版.md 13.02 KB
一键复制 编辑 原始数据 按行查看 历史

常见Java集合面试题及答案汇总,2021年底最新版

全部面试题答案,更新日期:01月30日,直接下载吧!

下载链接:高清500+份面试题资料及电子书,累计 10000+ 页大厂面试题 PDF

Java 集合

题1:Java 中如何判断 “java.util.LinkedList” 字符串实现 List 接口?

格式:判断一个类实现某接口

if(对象名 instanceof 接口名){
 
}

实例:判断“java.util.LinkedList”字符串实现List接口

String classes = "java.util.LinkedList";

if(Class.forName(classes).newInstance() instanceof List) {
	System.out.println(true);
}else {
	System.out.println(false);
}

题2:Java 中常用的并发集合有哪些?

并发List

Vector和CopyOnWriteArrayList是两个线程安全的List,Vector读写操作都用了同步,相对来说更适用于写多读少的场合,CopyOnWriteArrayList在写的时候会复制一个副本,对副本写,写完用副本替换原值,读的时候不需要同步,适用于写少读多的场合。

并发Set

CopyOnWriteArraySet基于CopyOnWriteArrayList来实现的,只是在不允许存在重复的对象这个特性上遍历处理了一下。

并发Map

ConcurrentHashMap是专用于高并发的Map实现,内部实现进行了锁分离,get操作是无锁的。

并发Queue

在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列。ConcurrentLinkedQueue适用于高并发场景下的队列,通过无锁的方式实现,通常ConcurrentLinkedQueue的性能要优于BlockingQueue。BlockingQueue的典型应用场景是生产者-消费者模式中,如果生产快于消费,生产队列装满时会阻塞,等待消费。

并发Dueue

Queue是一种双端队列,它允许在队列的头部和尾部进行出队和入队的操作。Dueue实现类有非线程安全的LinkedList、ArrayDueue和线程安全的LinkedBlockingDueue。LinkedBlockingDueue没有进行读写锁的分离,因此同一时间只能有一个线程对其操作,因此在高并发应用中,它的性能要远远低于LinkedBlockingQueue,更低于ConcurrentLinkedQueue。

并发锁重入锁ReentrantLock

ReentrantLock是一种互斥锁的实现,就是一次最多只能一个线程拿到锁;

读写锁ReadWriteLock

读写锁有读取和写入两种锁,读取锁允许多个读取的线程同时持有,而写入锁只能有一个线程持有。

条件Condition

调用Condition对象的相关方法,可以方便的挂起和唤醒线程。

题3:HashMap 超出负载因子 0.75 时有什么操作?

默认的负载因子大小为0.75,也就是指当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

通俗的讲就是如果超过阙值会进行扩容操作,扩容后的数组大小是原数组的2倍,将原来的元素重新hashing放入到新的散列表中去。

题4:Java 中如何实现单链表的反转?

举例

链表: 1->2->3->4 反转之后: 4->2->2->1

解题思路

从头到尾遍历原链表,每遍历一个结点,将其摘下放在新链表的最前端。注意链表为空和只有一个结点的情况。时间复杂度为O(n)

此部分代码借助“Java精选面试题”微信小程序中“Java 中如何单链表的创建和遍历”试题的代码,代码实现

import java.util.Stack;

public class LinkList {
	public Node head;
	public Node current;

	class Node {
		int data; // 数据域
		Node next;// 指针域

		public Node(int data) {
			this.data = data;
		}
	}

	// 方法:从尾到头打印单链表
	public void reversePrint(Node head) {

		if (head == null) {
			return;
		}

		Stack<Node> stack = new Stack<Node>(); // 新建一个栈
		Node current = head;

		// 将链表的所有结点压栈
		while (current != null) {
			stack.push(current); // 将当前结点压栈
			current = current.next;
		}

		// 将栈中的结点打印输出即可
		while (stack.size() > 0) {
			System.out.println(stack.pop().data); // 出栈操作
		}
	}
}

题5:Java 中如何创建和遍历单链表?

public class LinkList {
	public Node head;
	public Node current;

	//方法:向链表中添加数据  
	public void add(int data) {
		// 判断链表为空
		if (head == null) {// 如果头结点为空,说明这个链表还没有创建,那就把新的结点赋给头结点
			head = new Node(data);
			current = head;
		} else {
			// 创建新的结点,放在当前节点的后面(把新的结点合链表进行关联)
			current.next = new Node(data);
			// 把链表的当前索引向后移动一位
			current = current.next; // 此步操作完成之后,current结点指向新添加的那个结点
		}
	}

	//方法:遍历链表(打印输出链表。方法的参数表示从节点node开始进行遍历  
	public void print(Node node) {
		if (node == null) {
			return;
		}

		current = node;
		while (current != null) {
			System.out.println(current.data);
			current = current.next;
		}
	}

	class Node {
		//注:此处的两个成员变量权限不能为private,因为private的权限是仅对本类访问。  
		int data; // 数据域
		Node next;// 指针域

		public Node(int data) {
			this.data = data;
		}
	}

	public static void main(String[] args) {
		LinkList list = new LinkList();
		//向LinkList中添加数据  
		for (int i = 0; i < 10; i++) {
			list.add(i);
		}

		list.print(list.head);// 从head节点开始遍历输出
	}

}

执行结果

0
1
2
3
4
5
6
7
8
9

查看上述代码,Node节点采用的是内部类来表示。使用内部类的最大好处是可以和外部类进行私有操作的互相访问。

内部类访问的特点是:内部类可以直接访问外部类的成员,包括私有;外部类要访问内部类的成员,必须先创建对象。

为了方便添加和遍历的操作,在LinkList类中添加一个成员变量current,用来表示当前节点的索引。

遍历链表的方法中,参数node表示从node节点开始遍历,不一定要从head节点遍历。

题6:Java 中如何查找单链表中的中间结点?

分析思路

设置两个指针first和second,两个指针同时向前走,second指针每次走两步,first指针每次走一步,直到second指针走到最后一个结点时,此时first指针所指的结点就是中间结点。

注意链表为空,链表结点个数为1和2的情况下,时间复杂度为O(n)。

// 方法:查找链表的中间结点
public Node findMidNode(Node head) {

	if (head == null) {
		return null;
	}

	Node first = head;
	Node second = head;
	// 每次移动时,让second结点移动两位,first结点移动一位
	while (second != null && second.next != null) {
		first = first.next;
		second = second.next.next;
	}

	// 直到second结点移动到null时,此时first指针指向的位置就是中间结点的位置
	return first;
}

通过上述代码可以看出,当n为偶数时,得到的中间结点是第n/2+1个结点。比如链表有6个节点时,得到的是第4个节点。

题7:Iterater 和 ListIterator 都有哪些区别?

1)Iterator可以遍历Set和List集合,而ListIterator只能遍历List集合。

2)ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向遍历。

3)ListIterator接口使用nextIndex()和previousIndex()方法可以获取当前的索引位置,而Iterator不具备此功能。

4)ListIterator和Iterator都可实现删除对象,但是ListIterator接口使用set()方法可以实现对象的修改,而Iierator仅能遍历,不能修改。

题8:有10 亿个 url,每个 url 大小小于 56B,要求去重,内存只给你4G

解题思路:

1、首先将给定的url调用hash方法计算出对应的hash的value,在10亿的url中相同url必然有着相同的value。

2、将文件的hash table放到第value%n台机器上。

3、value/n是机器上hash table的值。

将文件分布在多个机器上,这样要处理网路延时。假设有n台机器。

首先hash文件得到hash value v 将文件的hash table 放到第v%n 台机器上。 v/n是机器上hash table的值。

问题分析:

将文件的url进行hash,得到值value,相同的url的文件具有相同的value,所以会被分配到同一台机器v%n上。在同一台机器上的重复的url文件具有相同的value/n值,如果出现了冲突,不同的url在同一台机器上也可能有相同的value/n值。在每个机器上将value/n值作为key,url值作为value构成hash表进行去重。最后将内存中去重后的hash表中的value值即url写入磁盘。合并磁盘中的各部分url文件,完成去重。

每个url地址大小为 56byte;

服务器内容大小为 4G =4*1024=4096kb=4096*1024 byte;

题9:HashSet 和 HashMap 有什么区别?

HashMap实现了Map接口,而HashSet实现了Set接口。

HashMap储存键值对,而HashSet仅仅存储对象。

HashMap使用put()方法将元素放入map中,而使用add()方法将元素放入set中。

HashMap中使用键对象来计算hashcode值,而HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false

HashMap比较快,因为是使用唯一的键来获取对象,而HashSet较HashMap来说比较慢。

题10:Java 中如何确保一个集合不能被修改?

可以使用Collections包下的unmodifiableMap()方法,通过这个方法返回的map是不可以修改的。这样改变集合的任何操作都会抛出Java.lang.UnsupportedOperationException异常。

Collections包也提供了对list和set集合的方法。

Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)

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

示例代码:

List<String> list = new ArrayList<>();
list. add("微信公众号");
Collection<String> unmlist = Collections.unmodifiableCollection(list);
unmlist.add("Java精选"); // 运行时此行报错
System.out.println(list.size());

题11:vector-和-arraylist-有什么区别和联系

题12:listsetcollectionmap有什么区别和联系

题13:java-中两个键-hashcode-相等如何获取对象

题14:java-中常见线程安全的-map-都有哪些

题15:hashmap-底层是如何实现的

题16:java-中-list-和-array-如何相互转换

题17:iterator-和-enumeration-接口有哪些区别

题18:java-中什么是-fail-fast

题19:泛型都有哪些规则

题20:java-中迭代集合如何避免-concurrentmodificationexception

题21:hashmap-是怎么扩容的

题22:hashmap-为什么多线程会导致死循环

题23:java-集合中都有哪些根接口

题24:hashmap-中-put-是如何实现的

题25:java-中如何优化-arraylist-集合插入万条数据量

大厂面试题

大厂面试题

大厂面试题

Java
1
https://gitee.com/AminDev/ebooks.git
git@gitee.com:AminDev/ebooks.git
AminDev
ebooks
Ebooks
master

搜索帮助