Java 哈希表知识(包括带源代码的大型工厂面试问题)
哈希表(Hash Table),也称为散列表,是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。在Java中,HashMap
是基于哈希表实现的,是Java集合框架中的一部分。理解哈希表的关键概念对于提高数据处理的效率至关重要。
核心知识点
-
哈希函数:
- 哈希表通过哈希函数将存储的键转换为数组的索引。理想情况下,哈希函数应将不同的键均匀分布在数组中,以减少碰撞(两个键映射到同一个索引)。
-
碰撞解决:
- 碰撞是指不同的键通过哈希函数得到了相同的索引。解决碰撞的方法主要有两种:
- 链地址法(分离链接法):每个数组元素指向一个链表,所有映射到同一索引的键都存储在这个链表中。
- **开放寻址
- 碰撞是指不同的键通过哈希函数得到了相同的索引。解决碰撞的方法主要有两种:
法**:当发生碰撞时,寻找数组中的下一个空槽位。常见的策略有线性探测、二次探测和双重散列。
-
负载因子:
- 负载因子是哈希表中元素个数与位置总数的比值。它是衡量哈希表满的程度的指标。当负载因子超过某个阈值时(例如0.75),为了减少碰撞和保持哈希表的操作效率,哈希表会进行扩容,即重新散列(Rehashing)。
-
重新散列(Rehashing):
- 当哈希表中的元素太多(即负载因子太高)时,会导致碰撞增多,操作效率下降。此时,哈希表会进行扩容,即创建一个更大的数组,并将所有现有的元素重新通过哈希函数映射到新数组中。
-
键的不可变性:
- 在使用如
HashMap
等基于哈希表的集合时,作为键的对象应该是不可变的。这是因为如果键对象在放入HashMap
之后改变了,它的哈希码也会改变,这将导致无法在哈希表中正确地找到该键。
- 在使用如
-
HashMap的工作原理:
-
HashMap
在Java中是通过数组+链表+红黑树(当链表长度超过阈值时转换为红黑树)实现的。它使用哈希函数将键映射到数组的一个位置上,如果出现碰撞,则将元素添加到该位置的链表中。
-
-
线程安全和ConcurrentHashMap:
- 标准的
HashMap
不是线程安全的,这意味着如果多个线程同时访问并修改一个HashMap
,可能会导致数据损坏。Java提供了ConcurrentHashMap
类,它是线程安全的,并通过分段锁(Segmentation)提高并发访问的效率。
- 标准的
应用场景
哈希表适用于那些需要快速访问、插入和删除元素的场景。例如:
- 实现高效的查找算法
- 去重
- 缓存(例如LRU缓存算法中的实现)
- 作为集合类的内部实现,如Java中的
HashSet
和HashMap
了解这些知识点有助于你在面试中更好地展示你对Java集合框架的理解,特别是在处理涉及数据存储和检索的问题时。为了帮助你准备面试,我会提供三道面试题目,涵盖算法、数据结构和系统设计,附带Java实现代码。这些题目旨在考察你的编程能力、问题解决技巧和对Java语言的熟练使用。
题目1:二叉树的右视图
题目描述:给定一个二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
解题思路:使用层序遍历(广度优先搜索)来解决这个问题。在遍历每一层的时候,记录下该层的最后一个元素。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public class BinaryTreeRightSideView {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> visibleValues = new ArrayList<>();
if (root == null) return visibleValues;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
for (int i = 0; i < levelSize; i++) {
TreeNode currentNode = queue.poll();
if (i == levelSize - 1) visibleValues.add(currentNode.val);
if (currentNode.left != null) queue.offer(currentNode.left);
if (currentNode.right != null) queue.offer(currentNode.right);
}
}
return visibleValues;
}
}
题目2:无重复字符的最长子串
题目描述:给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。
解题思路:使用滑动窗口技术来增加或减少子字符串的大小,并使用哈希集合来检测子字符串中是否有重复字符。
import java.util.HashSet;
import java.util.Set;
public class LongestSubstringWithoutRepeatingCharacters {
public int lengthOfLongestSubstring(String s) {
Set<Character> chars = new HashSet<>();
int left = 0, maxLength = 0;
for (int right = 0; right < s.length(); right++) {
while (chars.contains(s.charAt(right))) {
chars.remove(s.charAt(left));
left++;
}
chars.add(s.charAt(right));
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
}
题目3:设计TinyURL
题目描述:设计一个系统,能够将长链接转换成短链接和反向转换。
解题思路:使用哈希表来存储长链接到短链接的映射,以及短链接到长链接的映射。使用递增的ID编码为短链接的一部分。
import java.util.HashMap;
import java.util.Map;
public class TinyURL {
Map<Integer, String> idToLongURL = new HashMap<>();
Map<String, Integer> longURLToId = new HashMap<>();
int id = 0;
// Encodes a URL to a shortened URL.
public String encode(String longUrl) {
if (longURLToId.containsKey(longUrl)) {
return "http://tinyurl.com/" + longURLToId.get(longUrl);
}
id++;
idToLongURL.put(id, longUrl);
longURLToId.put(longUrl, id);
return "http://tinyurl.com/" + id;
}
// Decodes a shortened URL to its original URL.
public String decode(String shortUrl) {
int id = Integer.parseInt(shortUrl.replace("http://tinyurl.com/", ""));
return idToLongURL.get(id);
}
}
这些题目涉及了算法、数据结构和系统设计的基本概念,希望这些示例能帮助你在面试中展示出你的编程能力和问题解决能力。祝你面试顺利!
上一篇: 网络安全(黑客攻击)--自学 2024
下一篇: Leetcode 1.