1. ConcurrentHashMap 和 HashTable 有哪些区别
- 原理
- HashTable:它继承自
Dictionary
类,是 Java 早期提供的线程安全哈希表。其线程安全的实现方式是对每个方法都使用synchronized
关键字进行同步。例如,在调用put
、get
等方法时,整个 HashTable 会被锁定,其他线程必须等待当前线程释放锁后才能访问该方法。
java
import java.util.Hashtable;public class HashTableExample {public static void main(String[] args) {Hashtable<String, Integer> hashtable = new Hashtable<>();// 线程安全的 put 操作hashtable.put("one", 1); }
}
- ConcurrentHashMap:
- Java 7 版本:采用分段锁机制,将整个哈希表分成多个
Segment
(段),每个Segment
相当于一个小的 HashTable,每个Segment
都有自己的锁。不同的Segment
可以被不同的线程同时访问,只要访问的不是同一个Segment
,就不会产生锁竞争,从而提高了并发性能。 - Java 8 版本:摒弃了分段锁,采用 CAS(Compare - And - Swap)和
synchronized
来保证并发操作的安全性。它使用数组 + 链表 + 红黑树的数据结构,在进行插入、删除和查找操作时,首先通过哈希值找到对应的桶,然后对桶首节点加锁,锁的粒度更小,性能更高。
- Java 7 版本:采用分段锁机制,将整个哈希表分成多个
java
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();// 并发安全的 put 操作concurrentHashMap.put("one", 1); }
}
- 要点
- 线程安全实现方式:HashTable 是全量同步,所有方法都加锁,同一时间只能有一个线程访问;ConcurrentHashMap 在 Java 7 中是分段同步,Java 8 中是细粒度同步,允许多个线程同时访问不同部分。
- 性能:在高并发场景下,ConcurrentHashMap 的性能远远优于 HashTable,因为 HashTable 的锁粒度太大,容易造成线程阻塞。
- 空值处理:HashTable 不允许键或值为
null
,而 ConcurrentHashMap 同样不允许键为null
,如果插入null
键会抛出NullPointerException
。
- 应用
- 可以深入了解 CAS 算法的底层原理,它是一种无锁算法,通过比较内存中的值和预期值,如果相等则更新,否则重试。
- 研究 Java 8 中 ConcurrentHashMap 在扩容、红黑树转换等方面的优化策略。
2. 什么是 LinkedHashMap
- 原理
LinkedHashMap
继承自 HashMap
,它在 HashMap
的基础上维护了一个双向链表。这个双向链表定义了元素的迭代顺序,默认情况下是插入顺序,即元素按照插入的先后顺序进行迭代;也可以通过构造函数将其设置为访问顺序,即最近访问的元素会被移动到链表尾部。
java
import java.util.LinkedHashMap;
import java.util.Map;public class LinkedHashMapExample {public static void main(String[] args) {// 创建一个按照插入顺序排序的 LinkedHashMapLinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();linkedHashMap.put("one", 1);linkedHashMap.put("two", 2);linkedHashMap.put("three", 3);for (Map.Entry<String, Integer> entry : linkedHashMap.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}}
}
- 要点
- 继承关系:继承自
HashMap
,具备HashMap
的基本特性,如快速的查找、插入和删除操作。 - 双向链表:通过双向链表维护元素顺序,使得元素可以按照插入顺序或访问顺序进行迭代。
- 访问顺序:如果设置为访问顺序,每次访问元素时,该元素会被移动到链表尾部,这在实现 LRU 缓存时非常有用。
- 应用
- 实现一个简单的 LRU 缓存,使用
LinkedHashMap
来存储缓存数据,当缓存满时,自动移除最久未使用的元素。
java
import java.util.LinkedHashMap;
import java.util.Map;public class LRUCache<K, V> extends LinkedHashMap<K, V> {private final int capacity;public LRUCache(int capacity) {// 第三个参数设置为 true 表示按照访问顺序排序super(capacity, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > capacity;}};this.capacity = capacity;}public static void main(String[] args) {LRUCache<Integer, Integer> cache = new LRUCache<>(2);cache.put(1, 1);cache.put(2, 2);System.out.println(cache.get(1)); // 返回 1cache.put(3, 3); // 该操作会使得关键字 2 作废System.out.println(cache.get(2)); // 返回 -1 (未找到)cache.put(4, 4); // 该操作会使得关键字 1 作废System.out.println(cache.get(1)); // 返回 -1 (未找到)System.out.println(cache.get(3)); // 返回 3System.out.println(cache.get(4)); // 返回 4}
}
3. LinkedHashMap 与 HashMap 有哪些区别
- 原理
- HashMap:基于哈希表实现,通过哈希函数将键映射到桶中,每个桶可以存储一个链表或红黑树(当链表长度超过 8 且数组长度大于 64 时,链表会转换为红黑树)。它不保证元素的顺序,每次迭代的顺序可能不同。
java
import java.util.HashMap;
import java.util.Map;public class HashMapExample {public static void main(String[] args) {HashMap<String, Integer> hashMap = new HashMap<>();hashMap.put("one", 1);hashMap.put("two", 2);hashMap.put("three", 3);for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}}
}
- LinkedHashMap:除了使用哈希表存储元素外,还使用双向链表维护元素的插入顺序或访问顺序。在插入元素时,不仅会将元素存储到哈希表中,还会将其添加到双向链表的尾部;在访问元素时,如果是访问顺序,会将该元素移动到链表尾部。
- 要点
- 顺序性:HashMap 不保证元素的顺序,而 LinkedHashMap 可以保证插入顺序或访问顺序。
- 性能:由于需要维护双向链表,LinkedHashMap 的插入和删除操作相对 HashMap 会稍慢一些,但在迭代元素时,LinkedHashMap 可以按照顺序快速迭代。
- 内存占用:LinkedHashMap 由于需要额外维护双向链表,会比 HashMap 占用更多的内存空间。
- 应用
- 对比它们在不同场景下的性能差异,例如在大数据量插入和频繁迭代的场景下,测试 LinkedHashMap 和 HashMap 的性能表现。
- 分析在使用 LinkedHashMap 时,不同的访问顺序设置对性能和内存的影响。
4. 什么是 HashSet
- 原理
HashSet
基于 HashMap
实现,它不允许存储重复的元素。HashSet
内部使用一个 HashMap
来存储元素,将元素作为键,值统一为一个静态常量对象 PRESENT
。当向 HashSet
中添加元素时,实际上是将该元素作为键插入到内部的 HashMap
中,值为 PRESENT
。
java
import java.util.HashSet;public class HashSetExample {public static void main(String[] args) {HashSet<String> hashSet = new HashSet<>();hashSet.add("one");hashSet.add("two");hashSet.add("three");for (String element : hashSet) {System.out.println(element);}}
}
- 要点
- 唯一性:不允许存储重复元素,通过
HashMap
的键的唯一性来保证。 - 实现基础:基于
HashMap
实现,利用HashMap
的哈希算法和存储结构来实现快速的查找和插入操作。 - 无序性:不保证元素的顺序,每次迭代的顺序可能不同。
- 应用
- 了解
HashSet
在去重场景中的应用,例如对一个包含重复元素的列表进行去重操作。
java
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;public class DuplicateRemoval {public static void main(String[] args) {List<String> listWithDuplicates = new ArrayList<>();listWithDuplicates.add("one");listWithDuplicates.add("two");listWithDuplicates.add("one");HashSet<String> set = new HashSet<>(listWithDuplicates);List<String> listWithoutDuplicates = new ArrayList<>(set);for (String element : listWithoutDuplicates) {System.out.println(element);}}
}
5. HashMap 与 HashSet 有什么区别
- 原理
- HashMap:存储键值对,通过键的哈希值来确定存储位置,允许键和值为
null
。它使用哈希表来存储元素,当发生哈希冲突时,会通过链表或红黑树来解决。
java
import java.util.HashMap;
import java.util.Map;public class HashMapExample {public static void main(String[] args) {HashMap<String, Integer> hashMap = new HashMap<>();hashMap.put("one", 1);hashMap.put("two", 2);System.out.println(hashMap.get("one"));}
}
- HashSet:只存储元素,基于
HashMap
实现,元素作为键存储,值为一个固定的常量对象。它不允许存储重复元素,通过HashMap
的键的唯一性来保证。
java
import java.util.HashSet;public class HashSetExample {public static void main(String[] args) {HashSet<String> hashSet = new HashSet<>();hashSet.add("one");hashSet.add("two");System.out.println(hashSet.contains("one"));}
}
- 要点
- 存储内容:HashMap 存储键值对,HashSet 只存储元素。
- 用途:HashMap 用于根据键查找值,HashSet 用于判断元素是否存在。
- 空值处理:HashMap 允许键和值为
null
,而 HashSet 允许存储一个null
元素。
- 应用
- 分析在不同业务场景下,如何选择使用 HashMap 或 HashSet,例如在存储用户信息(键为用户 ID,值为用户对象)时使用 HashMap,在存储不重复的单词列表时使用 HashSet。
- 研究
HashMap
和HashSet
在处理哈希冲突时的性能差异。
6. 什么是 Collections.sort,内部原理
- 原理
Collections.sort
是 Java 集合框架中用于对列表进行排序的静态方法。它有两种重载形式:
- 对于实现了
RandomAccess
接口的列表(如ArrayList
),使用双轴快速排序(Dual - pivot Quicksort)。双轴快速排序是一种改进的快速排序算法,它选择两个轴元素,将数组分为三个部分,从而减少了比较和交换的次数,平均时间复杂度为 O(nlogn)。 - 对于未实现
RandomAccess
接口的列表(如LinkedList
),先将列表元素复制到一个数组中,对数组进行排序,再将排序后的元素复制回列表。
java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class CollectionsSortExample {public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(3);list.add(1);list.add(2);Collections.sort(list);for (Integer num : list) {System.out.println(num);}}
}
- 要点
- 排序算法:根据列表类型选择不同的排序算法,对于随机访问列表使用双轴快速排序,对于非随机访问列表使用先复制到数组再排序的方式。
- 时间复杂度:平均为 O(nlogn),在最坏情况下为 O(n2),但双轴快速排序的最坏情况很少出现。
- 稳定性:
Collections.sort
是不稳定的排序算法,即相等元素的相对顺序可能会改变。
- 应用
- 了解双轴快速排序的具体实现细节,以及它与传统快速排序的区别和优势。
- 研究其他排序算法(如归并排序、堆排序)的特点,并与双轴快速排序进行性能比较。
7. 什么是 hash 算法
- 原理
哈希算法是一种将任意长度的输入数据转换为固定长度的输出数据的算法,输出数据通常称为哈希值或散列值。哈希算法具有以下特点:
- 确定性:相同的输入始终产生相同的输出。
- 高效性:计算哈希值的速度快,通常在常数时间内完成。
- 均匀性:输出值在哈希空间中均匀分布,尽量减少哈希冲突的发生。
- 抗碰撞性:尽量避免不同的输入产生相同的输出,但在理论上,由于输入空间无限大,输出空间有限,哈希冲突是不可避免的。
- 要点
- 作用:用于数据的快速查找、存储和验证。例如,在哈希表中,通过哈希算法将键映射到桶中,实现快速的查找和插入操作;在文件验证中,通过计算文件的哈希值来验证文件的完整性。
- 特性:确定性、高效性、均匀性和抗碰撞性是哈希算法的重要特性。
- 应用场景:广泛应用于密码学、数据存储、缓存等领域。
- 应用
- 了解常见的哈希算法(如 MD5、SHA - 1、SHA - 256)的原理和应用场景。例如,MD5 曾经广泛用于文件校验,但由于其存在安全漏洞,现在逐渐被弃用;SHA - 256 常用于区块链等领域。
- 研究哈希碰撞的处理方法,如开放寻址法、链地址法等。
8. 什么是迭代器 Iterator 和 Enumeration
- 原理
- Iterator:是 Java 集合框架中用于遍历集合元素的接口,它提供了
hasNext()
、next()
和remove()
方法。hasNext()
用于判断集合中是否还有下一个元素,next()
用于返回下一个元素,remove()
用于删除最后一次调用next()
方法返回的元素。Iterator
可以在遍历过程中安全地删除元素。
java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class IteratorExample {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("one");list.add("two");list.add("three");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String element = iterator.next();if (element.equals("two")) {iterator.remove();}}for (String element : list) {System.out.println(element);}}
}
- Enumeration:是 Java 早期版本中用于遍历集合元素的接口,它只提供了
hasMoreElements()
和nextElement()
方法。hasMoreElements()
用于判断集合中是否还有更多元素,nextElement()
用于返回下一个元素。Enumeration
不支持在遍历过程中删除元素。
java
import java.util.Enumeration;
import java.util.Vector;public class EnumerationExample {public static void main(String[] args) {Vector<String> vector = new Vector<>();vector.add("one");vector.add("two");vector.add("three");Enumeration<String> enumeration = vector.elements();while (enumeration.hasMoreElements()) {String element = enumeration.nextElement();System.out.println(element);}}
}
- 要点
- 功能差异:Iterator 支持删除元素,Enumeration 不支持。
- 使用场景:Iterator 是推荐的遍历方式,适用于大多数集合类;Enumeration 主要用于旧代码的兼容性,在新代码中尽量避免使用。
- 线程安全:
Enumeration
是线程安全的,因为它没有remove()
方法,不会改变集合的结构;而Iterator
在多线程环境下使用时需要注意线程安全问题。
- 应用
- 了解迭代器模式的原理和应用,它是一种行为设计模式,用于提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
- 研究
Iterator
的子类(如ListIterator
)的特点,ListIterator
是Iterator
的子接口,它可以双向遍历列表,并且支持在遍历过程中添加、修改和删除元素。
9. 什么是 LIST,ArrayList,LinkedList 和 Vector 的区别和实现原理
- 原理
- ArrayList:基于动态数组实现,它会自动扩容以容纳更多元素。当元素数量超过数组容量时,会创建一个更大的数组,并将原数组元素复制到新数组中。默认初始容量为 10,每次扩容为原来的 1.5 倍。
java
import java.util.ArrayList;
import java.util.List;public class ArrayListExample {public static void main(String[] args) {List<String> arrayList = new ArrayList<>();arrayList.add("one");arrayList.add("two");System.out.println(arrayList.get(0));}
}
- LinkedList:基于双向链表实现,每个节点包含数据和指向前一个节点和后一个节点的引用。插入和删除操作只需要修改节点的引用,不需要移动大量元素,因此在插入和删除操作频繁的场景下性能较好。
java
import java.util.LinkedList;
import java.util.List;public class LinkedListExample {public static void main(String[] args) {List<String> linkedList = new LinkedList<>();linkedList.add("one");linkedList.add("two");System.out.println(linkedList.get(0));}
}
- Vector:也是基于动态数组实现,与
ArrayList
类似,但它是线程安全的,所有方法都使用synchronized
关键字进行同步。默认初始容量为 10,每次扩容为原来的 2 倍。
java
import java.util.Vector;
import java.util.List;public class VectorExample {public static void main(String[] args) {List<String> vector = new Vector<>();vector.add("one");vector.add("two");System.out.println(vector.get(0));}
}
- 要点
- 数据结构:ArrayList 和 Vector 是数组,支持随机访问,通过索引可以快速访问元素;LinkedList 是链表,不支持随机访问,需要从头节点或尾节点开始遍历。
- 线程安全:Vector 是线程安全的,ArrayList 和 LinkedList 不是。在多线程环境下,如果需要线程安全的列表,可以使用 Vector 或使用
Collections.synchronizedList
方法将 ArrayList 转换为线程安全的列表。 - 性能:ArrayList 和 Vector 随机访问速度快,插入和删除操作在尾部以外的位置较慢;LinkedList 插入和删除操作快,随机访问速度慢。
- 内存占用:ArrayList 和 Vector 由于是数组,会预先分配一定的内存空间,可能会造成内存浪费;LinkedList 每个节点需要额外的引用,会占用更多的内存空间。
应用
- 分析在不同场景下如何选择使用 ArrayList、LinkedList 和 Vector,例如在需要频繁随机访问元素时使用 ArrayList,在需要频繁插入和删除元素时使用 LinkedList,在多线程环境下使用 Vector。
- 研究 ArrayList 和 Vector 在扩容机制上的差异对性能的影响。
10. 什么是 volatile 和 synchronized,有什么区别
- 原理
- volatile:是一个关键字,用于修饰变量。它保证了变量的可见性,即当一个线程修改了被
volatile
修饰的变量的值,其他线程能够立即看到最新的值。这是因为volatile
变量会直接从主内存中读取和写入,而不是从线程的本地缓存中读取和写入。但它不保证原子性,例如对volatile
变量进行自增操作不是原子操作。
java
public class VolatileExample {private static volatile int counter = 0;public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter++;}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {counter++;}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(counter);}
}
- synchronized:是一个关键字,用于修饰方法或代码块。它可以保证在同一时间只有一个线程可以访问被
synchronized
修饰的方法或代码块,从而保证了线程安全,既保证了可见性,也保证了原子性。当一个线程进入synchronized
方法或代码块时,会获得对象的锁,其他线程必须等待该线程释放锁后才能进入。
java
public class SynchronizedExample {private static int counter = 0;public static synchronized void increment() {counter++;}public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(counter);}
}
- 要点
- 功能:volatile 保证可见性,synchronized 保证可见性和原子性。
- 使用场景:volatile 适用于一个变量被多个线程读取,一个线程写入的场景,例如状态标志位;synchronized 适用于多个线程对共享资源进行读写操作的场景,例如多线程对计数器进行自增操作。
- 性能:volatile 的性能开销较小,因为它不会阻塞线程;synchronized 的性能开销较大,因为它会导致线程阻塞。
- 应用
- 了解 Java 内存模型(JMM)中关于可见性和原子性的原理,以及
volatile
和synchronized
在 JMM 中的实现机制。 - 研究
volatile
和synchronized
在不同并发场景下的优化使用,例如使用volatile
结合 CAS 操作实现无锁算法。
友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读
https://download.csdn.net/download/ylfhpy/90498379