在Java中,"map" 是一个非常重要的接口,它属于 java.util
包。Map 是一种将键(key)映射到值(value)的对象,一个键可以最多映射到最多一个值。这意味着,Map 接口的实现(如 HashMap、TreeMap 等)提供了一种存储键值对(key-value pairs)的方式,其中每个键都是唯一的。
Map 方法:
Map 接口提供了多种方法用于插入、检索、删除键值对以及遍历 Map 中的元素。这里是一些常用的 Map 方法:
put(K key, V value)
put(K key, V value)
: 将指定的值与此映射中的指定键关联(可选操作)。Map
map = new HashMap<>(); map.put("apple", 100); // 将键 "apple" 与值 100 关联
get(Object key)
get(Object key)
: 返回指定键所映射的值;如果此映射不包含该键的映射,则返回null
。Integer value = map.get("apple"); // 返回与键 "apple" 关联的值,即 100 if (value != null) { System.out.println("The value of 'apple' is: " + value); } else { System.out.println("The key 'apple' does not exist in the map."); }
remove(Object key)
remove(Object key)
: 如果存在(即以前或显式地)将指定键映射到此映射,则将其删除(可选操作)。map.remove("apple"); // 删除键 "apple" 及其对应的值 if (!map.containsKey("apple")) { System.out.println("The key 'apple' has been removed from the map."); }
containsKey(Object key)
containsKey(Object key)
: 如果此映射包含指定键的映射,则返回true
。if (map.containsKey("banana")) { System.out.println("The map contains the key 'banana'."); } else { System.out.println("The map does not contain the key 'banana'."); }
containsValue(Object value)
containsValue(Object value)
: 如果此映射将一个或多个键映射到指定值,则返回true
。if (map.containsValue(100)) { System.out.println("The map contains the value 100."); } else { System.out.println("The map does not contain the value 100."); }
entrySet()
entrySet()
: 返回包含此映射中所包含的映射的Set
视图。for (Map.Entry
entry : map.entrySet()) { System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); }
keySet()
keySet()
: 返回此映射中包含的键的Set
视图。for (String key : map.keySet()) { System.out.println("Key = " + key); }
values()
values()
: 返回此映射中包含的值的Collection
视图。for (Integer value : map.values()) { System.out.println("Value = " + value); }
Map 接口的实现类,如 HashMap
和 TreeMap
,提供了不同的性能特性和排序保证。HashMap
提供了快速的键值查找能力,但不保证映射的顺序;而 TreeMap
则基于红黑树实现,可以保证键的自然顺序或创建映射时提供的 Comparator
进行排序。
HashMap
- 在Java中,
HashMap
是java.util
包下的一个非常重要的类,它实现了Map
接口。HashMap
存储键值对(key-value pairs),其中每个键都是唯一的,并且映射到最多一个值。HashMap
允许使用null
值和null
键(但最多只能有一个null
键)。HashMap
提供了快速的键值查找能力,因为它基于哈希表实现。不过,需要注意的是,HashMap
不保证映射的顺序;特别是,它不保证随着时间的推移顺序保持不变。
- 基于哈希表实现:HashMap内部通过哈希表来存储键值对,这使得它能够提供快速的键值查找能力。
- 不保证映射顺序:HashMap不保证映射的顺序;特别是,它不保证随着时间的推移顺序保持不变。当你遍历HashMap时,元素的顺序可能会发生变化。
- 允许使用null键和null值:HashMap允许最多一个null键和多个null值。这意呀着你可以将null作为键或值存储在HashMap中,但请注意,由于键的唯一性,HashMap中只能有一个null键。
- 键的唯一性:HashMap中的每个键都是唯一的,这意味着两个不同的键不能映射到相同的值(尽管不同的键可以映射到相同的值,即值可以是重复的)。
- 性能高效:由于HashMap基于哈希表实现,并且具有较好的哈希函数和冲突解决策略(如链地址法),因此它在大多数情况下能够提供常数时间复杂度的查找、插入和删除操作(平均情况下)。然而,在最坏的情况下(如哈希冲突非常严重时),这些操作的时间复杂度可能会退化到O(n)。
- 线程不安全:HashMap不是线程安全的。如果在多线程环境中需要共享HashMap,那么需要额外的同步措施,或者使用
ConcurrentHashMap
等线程安全的替代方案。这些特点使得HashMap成为在Java中存储键值对时的一个非常流行和有用的选择,特别是在不需要保持映射顺序的场景下。
HashMap
的一些常用方法:
put(K key, V value)
: 将指定的值与此映射中的指定键关联(可选操作)。get(Object key)
: 返回指定键所映射的值;如果此映射不包含该键的映射,则返回null
。remove(Object key)
: 如果存在(即以前或显式地)将指定键映射到此映射,则将其删除(可选操作)。containsKey(Object key)
: 如果此映射包含指定键的映射,则返回true
。containsValue(Object value)
: 如果此映射将一个或多个键映射到指定值,则返回true
。
HashMap
演示这些方法:import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
// 创建一个HashMap实例
Map map = new HashMap<>();
// 使用put方法添加键值对
map.put("apple", 100);
map.put("banana", 200);
// 使用get方法获取键对应的值
Integer value = map.get("apple");
if (value != null) {
System.out.println("The value of 'apple' is: " + value);
} else {
System.out.println("The key 'apple' does not exist in the map.");
}
// 使用remove方法删除键值对
map.remove("banana");
if (!map.containsKey("banana")) {
System.out.println("The key 'banana' has been removed from the map.");
}
// 使用containsKey方法检查键是否存在
if (map.containsKey("apple")) {
System.out.println("The map contains the key 'apple'.");
}
// 使用containsValue方法检查值是否存在
if (map.containsValue(100)) {
System.out.println("The map contains the value 100.");
}
// 遍历HashMap
// 使用keySet遍历键
for (String key : map.keySet()) {
System.out.println("Key: " + key);
}
// 使用values遍历值
for (Integer val : map.values()) {
System.out.println("Value: " + val);
}
// 使用entrySet遍历键值对
for (Map.Entry entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
}
}
在这个例子中,我们首先创建了一个
HashMap
实例,并使用put
方法添加了两个键值对("apple" -> 100 和 "banana" -> 200)。然后,我们使用get
方法获取了键"apple"对应的值,并打印出来。接着,我们使用remove
方法删除了键"banana"及其对应的值,并检查它是否已被删除。我们还展示了如何使用containsKey
和containsValue
方法来检查键和值是否存在。最后,我们通过遍历keySet
、values
和entrySet
来展示了如何遍历HashMap
中的键、值和键值对。
这个例子将涉及到
HashMap
的高级用法,包括遍历、条件查找、以及结合其他数据结构如List
来解决问题。
假设我们有一个场景,我们需要管理一个学校的课程信息,包括课程的ID、名称、以及选修该课程的学生列表。为了高效地处理这些信息,我们可以使用
HashMap
来存储课程ID到课程信息的映射,其中课程信息是一个包含课程名称和学生列表的对象。
首先,我们定义一个简单的CourseInfo
类来表示课程信息:
import java.util.ArrayList;
import java.util.List;
public class CourseInfo {
private String courseName;
private List students;
public CourseInfo(String courseName) {
this.courseName = courseName;
this.students = new ArrayList<>();
}
public void addStudent(String studentName) {
students.add(studentName);
}
public String getCourseName() {
return courseName;
}
public List getStudents() {
return students;
}
@Override
public String toString() {
return "CourseInfo{" +
"courseName='" + courseName + '\'' +
", students=" + students +
'}';
}
}
然后,我们使用HashMap
来管理课程信息:
import java.util.HashMap;
import java.util.Map;
public class SchoolCourses {
private Map courses;
public SchoolCourses() {
this.courses = new HashMap<>();
}
public void addCourse(String courseId, String courseName) {
if (!courses.containsKey(courseId)) {
courses.put(courseId, new CourseInfo(courseName));
}
}
public void addStudentToCourse(String courseId, String studentName) {
if (courses.containsKey(courseId)) {
courses.get(courseId).addStudent(studentName);
} else {
System.out.println("Course ID does not exist.");
}
}
// 示例:遍历所有课程并打印信息
public void printAllCourses() {
for (Map.Entry entry : courses.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
// 示例:根据课程ID查找并打印课程信息
public void printCourseInfo(String courseId) {
if (courses.containsKey(courseId)) {
System.out.println(courses.get(courseId));
} else {
System.out.println("Course ID does not exist.");
}
}
public static void main(String[] args) {
SchoolCourses schoolCourses = new SchoolCourses();
schoolCourses.addCourse("CS101", "Introduction to Computer Science");
schoolCourses.addStudentToCourse("CS101", "Alice");
schoolCourses.addStudentToCourse("CS101", "Bob");
schoolCourses.addCourse("MATH101", "Mathematics for Computer Science");
schoolCourses.addStudentToCourse("MATH101", "Charlie");
schoolCourses.printAllCourses();
schoolCourses.printCourseInfo("CS101");
}
}
在这个例子中,
SchoolCourses
类使用HashMap
来管理课程信息。我们定义了addCourse
方法来添加新课程,addStudentToCourse
方法来为指定课程添加学生,以及printAllCourses
和printCourseInfo
方法来遍历和打印课程信息。这个示例展示了如何在复杂场景中有效地使用HashMap
来管理和检索数据。
HashMap
的性能特性使其成为在不需要保持映射顺序的场景下存储键值对的理想选择。然而,如果你需要保持键的自然顺序或根据创建映射时提供的 Comparator
进行排序,那么 TreeMap
可能是更好的选择。
TreeMap
- TreeMap是Java中的一个非常有用的数据结构,它实现了
SortedMap
接口,这意味着它不仅能够存储键值对,还能确保所有的键都处于排序状态。TreeMap基于红黑树实现,这保证了它具有良好的查找、插入和删除性能,时间复杂度通常为O(log n)。- 在TreeMap中,每个键都必须实现
Comparable
接口,或者你可以在创建TreeMap时提供一个Comparator
来指定键的排序方式。如果没有提供Comparator,那么键的自然顺序将被用来排序。
- 它保证映射按照键的排序顺序进行视图迭代(包括分割器提供的弱视图)。
- 它不允许有重复的键;每个键最多只能映射到一个值。
- 它实现了
NavigableMap
接口,提供了比SortedMap
更多的导航方法,如firstKey()
、lastKey()
、headMap(K toKey)
等。TreeMap非常适合于需要按键排序的映射场景,比如存储学生成绩、员工信息等,并且需要经常进行范围查询或排序操作。
put(K key, V value): 将指定的值与此映射中的指定键关联(可选操作)。如果映射以前包含该键的映射,则旧值将被替换。
get(Object key): 返回指定键所映射的值;如果此映射不包含该键的映射,则返回
null
。remove(Object key): 如果存在(即以前或显式地)将指定键映射到此映射,则将其删除(可选操作)。
containsKey(Object key): 如果此映射包含指定键的映射,则返回
true
。containsValue(Object value): 如果此映射将一个或多个键映射到指定值,则返回
true
。firstKey(): 返回此映射中当前第一个(最低)键。
lastKey(): 返回此映射中当前最后一个(最高)键。
headMap(K toKey): 返回此映射中部分视图,其键小于
toKey
(不包含toKey
)。tailMap(K fromKey): 返回此映射中部分视图,其键大于或等于
fromKey
。subMap(K fromKey, K toKey): 返回此映射中部分视图,其键的范围从
fromKey
(包含)到toKey
(不包含)。entrySet(): 返回包含此映射中所包含的映射的
Set
视图。该集合的元素是Map.Entry
对象,它们反映了映射的迭代顺序。keySet(): 返回此映射中包含的键的
Set
视图。该集合的元素按映射的排序顺序排列。values(): 返回此映射中包含的值的
Collection
视图。这个集合包含的元素是按映射的键排序顺序对应的值。请注意,
TreeMap
的键是有序的,这是它与HashMap
(无序)的主要区别之一。当你需要对键进行排序或频繁地进行范围查询时,TreeMap
是非常有用的。
TreeMap
中这些方法的基本使用:import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapExample {
public static void main(String[] args) {
// 创建一个TreeMap实例
TreeMap treeMap = new TreeMap<>();
// 使用put方法添加键值对
treeMap.put(1, "One");
treeMap.put(3, "Three");
treeMap.put(2, "Two");
// 使用get方法获取键对应的值
System.out.println("The value for key 2 is: " + treeMap.get(2));
// 使用remove方法删除键值对
treeMap.remove(3);
// 使用containsKey方法检查键是否存在
System.out.println("Does the map contain key 1? " + treeMap.containsKey(1));
// 使用containsValue方法检查值是否存在
System.out.println("Does the map contain value 'Three'? " + treeMap.containsValue("Three")); // 这将返回false,因为'Three'已被删除
// 使用firstKey和lastKey方法获取第一个和最后一个键
System.out.println("First key: " + treeMap.firstKey());
System.out.println("Last key: " + treeMap.lastKey());
// 使用headMap获取小于某个键的所有键值对
Map head = treeMap.headMap(2);
System.out.println("Head map (keys < 2): " + head);
// 使用tailMap获取大于或等于某个键的所有键值对
Map tail = treeMap.tailMap(2);
System.out.println("Tail map (keys >= 2): " + tail);
// 使用subMap获取键在某个范围内的所有键值对
Map sub = treeMap.subMap(1, 3);
System.out.println("Sub map (keys 1 to 2): " + sub);
// 使用entrySet获取所有键值对的集合
Set> entries = treeMap.entrySet();
for (Map.Entry entry : entries) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
// 使用keySet获取所有键的集合
Set keys = treeMap.keySet();
for (Integer key : keys) {
System.out.println("Key: " + key);
}
// 使用values获取所有值的集合
java.util.Collection values = treeMap.values();
for (String value : values) {
System.out.println("Value: " + value);
}
}
}
在这个例子中,创建了一个
TreeMap
实例,并使用put
方法添加了一些键值对。然后,展示了如何使用get
、remove
、containsKey
、containsValue
、firstKey
、lastKey
、headMap
、tailMap
、subMap
、entrySet
、keySet
和values
方法来操作这个映射。注意,由于TreeMap
保持键的排序顺序,因此当你遍历键、值或键值对时,它们将按照排序顺序出现。
在这个示例中,使用
TreeMap
来管理一个图书馆中书籍的借阅记录,其中书籍的ISBN作为键,借阅记录作为值。借阅记录将是一个简单的类,包含借阅者的姓名和借阅日期。
首先,我们定义借阅记录类BorrowRecord
:
import java.time.LocalDate;
public class BorrowRecord {
private String borrowerName;
private LocalDate borrowDate;
public BorrowRecord(String borrowerName, LocalDate borrowDate) {
this.borrowerName = borrowerName;
this.borrowDate = borrowDate;
}
public String getBorrowerName() {
return borrowerName;
}
public LocalDate getBorrowDate() {
return borrowDate;
}
@Override
public String toString() {
return "BorrowRecord{" +
"borrowerName='" + borrowerName + '\'' +
", borrowDate=" + borrowDate +
'}';
}
}
然后,我们使用TreeMap
来管理书籍的借阅记录:
import java.time.LocalDate;
import java.util.TreeMap;
public class LibraryBorrowRecords {
private TreeMap borrowRecords;
public LibraryBorrowRecords() {
// 使用自然顺序(String的字典序)来排序ISBN
this.borrowRecords = new TreeMap<>();
}
// 添加借阅记录
public void addBorrowRecord(String isbn, String borrowerName, LocalDate borrowDate) {
borrowRecords.put(isbn, new BorrowRecord(borrowerName, borrowDate));
}
// 查找并返回指定ISBN的借阅记录
public BorrowRecord getBorrowRecord(String isbn) {
return borrowRecords.get(isbn);
}
// 遍历并打印所有借阅记录
public void printAllBorrowRecords() {
for (Map.Entry entry : borrowRecords.entrySet()) {
System.out.println("ISBN: " + entry.getKey() + ", " + entry.getValue());
}
}
public static void main(String[] args) {
LibraryBorrowRecords library = new LibraryBorrowRecords();
// 添加一些借阅记录
library.addBorrowRecord("978-1234567890", "Alice", LocalDate.of(2023, 10, 1));
library.addBorrowRecord("978-0987654321", "Bob", LocalDate.of(2023, 10, 5));
library.addBorrowRecord("978-1122334455", "Charlie", LocalDate.of(2023, 10, 10));
// 打印所有借阅记录
library.printAllBorrowRecords();
// 查找并打印特定ISBN的借阅记录
BorrowRecord record = library.getBorrowRecord("978-1234567890");
if (record != null) {
System.out.println("Borrow record for ISBN 978-1234567890: " + record);
} else {
System.out.println("No borrow record found for ISBN 978-1234567890.");
}
}
}
在这个示例中,我们创建了一个
LibraryBorrowRecords
类来管理借阅记录,其中使用了TreeMap
来存储ISBN到借阅记录的映射。我们定义了添加借阅记录、查找借阅记录和打印所有借阅记录的方法。在main
方法中,我们创建了一个LibraryBorrowRecords
实例,添加了一些借阅记录,并展示了如何遍历和查找特定的借阅记录。