1 Star 0 Fork 31

tankai / Ebooks

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

最新Java集合面试题及答案附答案汇总

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

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

Java 集合

题1:HashMap 为什么多线程会导致死循环?

HashMap多线程会导致死循环的主要原因在于并发下的Rehash()方法会造成元素之间会形成一个循环链表。

注意的是JDK1.8及以上解决了这个问题,但是不建议在多线程下使用HashMap,因为多线程下使用HashMap还是会存在其他问题比如数据丢失。在并发环境下推荐使用ConcurrentHashMap,后续篇幅中会写相关的文章。

题2:什么是 HashMap?

HashMap实现了Map接口,Map接口对键值对进行映射。

Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。

HashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,能保证只有一个线程更改Map。

public Object put(Object Key,Object value);

该方法用来将元素添加到map中。

题3:Java 中如何快速删除链表中某个节点?

假设给定一个链表,删除链表中倒数第n个节点,并且返回链表的头节点。

实例:

给定一个链表: 1->2->3->4->5,和n=2。

当删除倒数第二个节点后,链表变为1->2->3->5。

分析:表明给定的n节点是有效的。

解题思路:

1、删除的节点可以通过双指针pre和index;

2、双指针开始都是指向头节点,index先走到比头节点大n的位置;

3、两个指针同时往后遍历,当index的下一个为空时,此时pre指向的就是需要删除的那一个节点;

4、删除这个节点还需要分析后续节点是否为空;

5、如果后续不为空需要后面的节点覆盖它,若为空,就只需要设置为null。

实现代码如下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(null == head){
            return null;
        }
        if(n <= 0){
            return head;
        }
        ListNode pre = head;
        ListNode index = head;
        ListNode orghead = head;
        for(int i = 1;i < n ;i++){   //让i先走n步
            index = index.next;
            if(null == index){
                return null;
            }
        }
        while(null != index.next){  //到n的位置,pre开始遍历
            orghead = pre;
            pre = pre.next;
            index = index.next;
        }
        //删除时得考虑删除节点是否有后续节点
        if(null != pre.next){
            pre.val = pre.next.val; //有就往前移
            pre.next = pre.next.next;
        }else{
            if(null == pre.next){ //没有后续,需把它设置为null值
                return null;
            }
            orghead.next = null;
        }
        return head;
    }
}

题4:HashMap 中如何实现同步?

HashMap可以使用如下代码实现:

Map map = Collections.synchronizedMap(new HashMap());

来达到同步的效果。

具体而言,该方法会返回一个同步的Map集合,这个Map封装了底层HashMap的所有方法,使得底层的HashMap可以在多线程的环境中也能够保证安全性。

题5:Vector 和 ArrayList 有什么区别和联系?

相同点:

1)实现原理相同,底层都使用数组。

2)功能相同,实现增删改查等操作的方法相似。

3)都是长度可变的数组结构,很多情况下可以互用。

不同点:

1)Vector是早期JDK版本提供,ArrayList是新版本替代Vector的。

2)Vector线程安全,ArrayList重速度轻安全,线程非安全。

长度需增长时,Vector默认增长一倍,ArrayList增长50%。

题6:泛型有什么使用场景?

当类中要操作的引用数据类型不确定时,在JDK1.5版本前使用Object来完成扩展,JDK1.5后推荐使用泛型来完成扩展,同时保证安全性。

题7:Java 中常见线程安全的 Map 都有哪些?

Java中常见线程安全的Map有Hashtable、synchronizedMap和ConcurrentHashMap。

Hashtable

使用方式,代码如下:

Map<String,Object> hashtable=new Hashtable<String,Object>();

查看源码可以看出put()、get()、containsKey()等方法都使用了synchronized关键字修饰实现同步,因此它是线程安全的。

public synchronized V put(K key, V value) {//部分源代码jdk1.8
	// Make sure the value is not null
	if (value == null) {
		throw new NullPointerException();
	}

	// Makes sure the key is not already in the hashtable.
	Entry<?,?> tab[] = table;
	int hash = key.hashCode();
	int index = (hash & 0x7FFFFFFF) % tab.length;
	@SuppressWarnings("unchecked")
	Entry<K,V> entry = (Entry<K,V>)tab[index];
	for(; entry != null ; entry = entry.next) {
		if ((entry.hash == hash) && entry.key.equals(key)) {
			V old = entry.value;
			entry.value = value;
			return old;
		}
	}

	addEntry(hash, key, value, index);
	return null;
}

@SuppressWarnings("unchecked")
public synchronized V get(Object key) {
	Entry<?,?> tab[] = table;
	int hash = key.hashCode();
	int index = (hash & 0x7FFFFFFF) % tab.length;
	for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
		if ((e.hash == hash) && e.key.equals(key)) {
			return (V)e.value;
		}
	}
	return null;
}

public synchronized boolean containsKey(Object key) {
	Entry<?,?> tab[] = table;
	int hash = key.hashCode();
	int index = (hash & 0x7FFFFFFF) % tab.length;
	for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
		if ((e.hash == hash) && e.key.equals(key)) {
			return true;
		}
	}
	return false;
}

SynchronizedMap

使用方式,代码如下:

Map<String,Object> synchronizedMap = Collections.synchronizedMap(new Hashtable<String,Object>());

查看源码可以看出其实是加了一个对象锁,在每次操作hashmap时都需要先获取这个对象锁,且这个对象锁使用了synchronized关键字修饰,锁的性能与Hashtable相差无几。

SynchronizedMap(Map<K,V> m, Object mutex) {//部分源代码jdk1.8
	this.m = m;
	this.mutex = mutex;
}

public int size() {
	synchronized (mutex) {return m.size();}
}
public boolean isEmpty() {
	synchronized (mutex) {return m.isEmpty();}
}
public boolean containsKey(Object key) {
	synchronized (mutex) {return m.containsKey(key);}
}
public boolean containsValue(Object value) {
	synchronized (mutex) {return m.containsValue(value);}
}
public V get(Object key) {
	synchronized (mutex) {return m.get(key);}
}

public V put(K key, V value) {
	synchronized (mutex) {return m.put(key, value);}
}
public V remove(Object key) {
	synchronized (mutex) {return m.remove(key);}
}

ConcurrentHashMap

使用方式,代码如下:

Map<String,Object> concurrentHashMap = new ConcurrentHashMap<String,Object>();

ConcurrentHashMap是目前使用最多的线程安全的集合,也是最推荐的一个集合。

查看源码可以发现线程安全是通过cas+synchronized+volatile来实现的,其中也可看出它的锁是分段锁,所以它的性能相对来说是比较好的,整体实现还是比较复杂的。

public V put(K key, V value) {//部分源代码jdk1.8
	return putVal(key, value, false);
}

/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
	if (key == null || value == null) throw new NullPointerException();
	int hash = spread(key.hashCode());
	int binCount = 0;
	for (Node<K,V>[] tab = table;;) {
		Node<K,V> f; int n, i, fh;
		if (tab == null || (n = tab.length) == 0)
			tab = initTable();
		else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
			if (casTabAt(tab, i, null,
						 new Node<K,V>(hash, key, value, null)))
				break;                   // no lock when adding to empty bin
		}
		else if ((fh = f.hash) == MOVED)
			tab = helpTransfer(tab, f);
		else {
			V oldVal = null;
			synchronized (f) {
				if (tabAt(tab, i) == f) {
					if (fh >= 0) {
						binCount = 1;
						for (Node<K,V> e = f;; ++binCount) {
							K ek;
							if (e.hash == hash &&
								((ek = e.key) == key ||
								 (ek != null && key.equals(ek)))) {
								oldVal = e.val;
								if (!onlyIfAbsent)
									e.val = value;
								break;
							}
							Node<K,V> pred = e;
							if ((e = e.next) == null) {
								pred.next = new Node<K,V>(hash, key,
														  value, null);
								break;
							}
						}
					}
					else if (f instanceof TreeBin) {
						Node<K,V> p;
						binCount = 2;
						if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
													   value)) != null) {
							oldVal = p.val;
							if (!onlyIfAbsent)
								p.val = value;
						}
					}
				}
			}
			if (binCount != 0) {
				if (binCount >= TREEIFY_THRESHOLD)
					treeifyBin(tab, i);
				if (oldVal != null)
					return oldVal;
				break;
			}
		}
	}
	addCount(1L, binCount);
	return null;
}

题8:Java 中如何获取 List 集合泛型类型?

实现获取List集合泛型类型,实例代码如下:

public static void main(String[] args) throws Exception{	
	Class<?> clazz = A.class;
	Field field = clazz.getField("lists");
	ParameterizedType type = (ParameterizedType) field.getGenericType();
	System.out.println(type.getActualTypeArguments()[0]);
}

class A {
	public List<String> lists = new ArrayList<>();
}

输出结果

class java.lang.String

题9:Java 中什么是 fail-safe?

fail-safe(安全失败)采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

题10: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个节点。

题11:java-中-list-集合如何排序

题12:java-中求单链表中节点的个数

题13:java-中如何合并两个有序单链表后依然有序

题14:comparable-和-comparator有什么区别

题15:什么是-hashset

题16:hashmap-长度为什么是2的幂次方

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

题18:java-中常用的并发集合有哪些

题19:java-中如何确保一个集合不能被修改

题20:java-中两个对象-hashcode-相等会产生什么问题-

题21:java-集合类框架的基本接口有哪些

题22:java-中遍历-list-集合都有哪些方式

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

题24:hashmap-和-hashtable-有什么区别

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

大厂面试题

大厂面试题

大厂面试题

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

搜索帮助

53164aa7 5694891 3bd8fe86 5694891