Map集合是Java集合框架中的一个重要组成部分,它以键值对(key-value pairs
)的形式存储数据。Map集合中的每个键都是唯一的,因此它不允许键的重复,而每个键可以关联一个值,同一键可以对应不同的值(在不改变键的情况下更新值)。Map
接口主要用于通过键快速查找、插入和删除对应的值。
Map接口的主要特征
- 键-值存储 :Map集合中每个元素都是一个键值对(KeyValuePair),通常写作
<K, V>
,其中K
代表键的类型,V
代表值的类型。 - 唯一键约束:Map集合的键必须唯一,也就是说,如果试图插入一个与现有键相同的键,原有的键值对将被新的键值对替换(如果值不同的话)。
- 键的无序性 :Map集合中的元素不保证顺序,除非使用了如
LinkedHashMap
这样的有序Map实现类。 - 常用操作 :Map集合提供了丰富的API来添加、删除、查找、更新键值对,如
put(key, value)
(添加或更新键值对)、get(key)
(通过键获取值)、remove(key)
(通过键删除键值对)、containsKey(key)
(检查Map中是否存在指定键)等。
Map接口的主要实现类
- HashMap :
- 基于哈希表的实现,它允许空键和空值。
- 迭代顺序不是基于插入顺序的。
- LinkedHashMap :
HashMap
的子类,维护了一个双向链表,可以按照插入顺序或访问顺序来遍历键值对。
- TreeMap :
- 基于红黑树的实现,可以按照自然顺序或自定义顺序对键进行排序。
- Hashtable :
- 和
HashMap
类似,但它是线程安全的,不允许空键和空值。
- 和
- ConcurrentHashMap :
- 线程安全的
HashMap
实现,用于多线程环境。
- 线程安全的
- IdentityHashMap :
- 使用
==
而不是equals()
方法来比较键。
- 使用
Map接口的主要方法
添加键值对
V put(K key, V value)
:向Map中添加键值对,如果键已存在,则旧值将被新值替换并返回旧值;如果键不存在,则添加新的键值对并返回null。
java
// 创建一个 Map 集合
Map<String, Integer> myMap = new HashMap<>();
// 添加键值对
myMap.put("Apple", 1);
myMap.put("Banana", 2);
System.out.println(myMap); // 输出: {Apple=1, Banana=2}
获取值
V get(Object key)
:根据给定的键获取对应的值,如果键不存在,则返回null。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 获取值
Integer banana = myMap.get("Banana");
System.out.println(banana); // 输出: 2
删除键值对
V remove(Object key)
:删除指定键的键值对,如果键存在,则返回对应的值,否则返回null。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 删除键为"Apple"的键值对,并返回1
Integer removedValue = myMap.remove("Apple");
System.out.println(removedValue);// 输出: 1
System.out.println(myMap);// 输出: {Pear=5, Mango=3, Orange=4, Banana=2}
判断键是否存在
boolean containsKey(Object key)
:判断Map中是否包含指定的键。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 判断 "Apple" 是否存在
boolean hasApple = myMap.containsKey("Apple");
System.out.println("键 Apple 是否存在: " + hasApple);// 输出: 键 Apple 是否存在: true
判断值是否存在
boolean containsValue(Object value)
:判断Map中是否包含指定的值。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 判断Value值 1 是否存在
boolean containsValue = myMap.containsValue(1);
System.out.println("值 1 是否存在? " + containsValue);// 输出: 值 1 是否存在? true
获取Map大小
int size()
:返回Map中键值对的数量。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 获取集合的大小
int mapSize = myMap.size();
System.out.println(mapSize);// 输出: 5
清空Map
void clear()
:删除Map中的所有键值对。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 清空Map
myMap.clear();
System.out.println(myMap);//输出: {}
获取所有键的集合
Set<K> keySet()
:返回一个包含所有键的Set集合。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 获得所有键的集合
Set<String> keys = myMap.keySet();
System.out.println(keys);//输出: [Apple, Pear, Mango, Orange, Banana]
获取所有值的集合
Collection<V> values()
:返回一个包含所有值的Collection集合。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 获取所有值的集合
Collection<Integer> values = myMap.values();
System.out.println(values);// 输出: [1, 5, 3, 4, 2]
获取键值对集合
Set<Map.Entry<K, V>> entrySet()
:返回一个包含所有键值对的Set集合,每个元素都是Map.Entry类型的对象。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<String, Integer> myMap =new HashMap<String, Integer>() {{
put("Apple", 1);
put("Banana", 2);
put("Mango", 3);
put("Orange", 4);
put("Pear", 5);
}};
// 获取键值对
Set<Map.Entry<String, Integer>> entries = myMap.entrySet();
System.out.println(entries);// 输出: [Apple=1, Pear=5, Mango=3, Orange=4, Banana=2]
Map集合遍历
使用 keySet()
遍历键值对
keySet()
方法返回一个包含所有键的Set视图,通过迭代这个Set可以获取到所有的键,再通过Map的get()
方法取得对应的值。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<Integer, String> myMap =new HashMap<Integer, String>() {{
put(1, "Apple");
put(2, "Banana");
put(3, "Mango");
put(4, "Orange");
put(5, "Pear");
}};
// 使用keySet遍历
for (Integer key : myMap.keySet()) {
String value = myMap.get(key);
System.out.println("Key: " + key + ", Value: " + value);
}
使用 entrySet()
遍历键值对
entrySet()
方法返回一个包含所有键值对(Map.Entry对象)的Set视图,可以直接通过Map.Entry对象获取键和值。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<Integer, String> myMap =new HashMap<Integer, String>() {{
put(1, "Apple");
put(2, "Banana");
put(3, "Mango");
put(4, "Orange");
put(5, "Pear");
}};
// 使用 entrySet() 遍历键值对
for (Map.Entry<Integer, String> entry : myMap.entrySet()) {
// 获取 键
Integer key = entry.getKey();
// 获取 值
String value = entry.getValue();
System.out.println("Key: " + key + ", Value: " + value);
}
使用 forEach()
forEach()
方法和Stream API
是在Java 8中引入的,提供了更加简洁的遍历方式,尤其在处理集合时可以轻松实现链式操作。
java
// 创建一个 Map 集合,并在构造函数中直接添加初始键值对
Map<Integer, String> myMap =new HashMap<Integer, String>() {{
put(1, "Apple");
put(2, "Banana");
put(3, "Mango");
put(4, "Orange");
put(5, "Pear");
}};
// 使用 forEach() 遍历输出
myMap.forEach((key,value)->System.out.println("Key: " + key + ", Value: " + value));
HashMap 集合
HashMap
是基于哈希表实现的Map
接口的一个非同步实现类,允许存储键值对(key-value pairs),并且可以高效地根据键来查找值。
- 数据结构 :
HashMap
内部采用数组和链表(或红黑树,自JDK1.8
开始)相结合的方式来存储数据。数组用于快速定位,链表或红黑树用于解决哈希冲突。 - 键的唯一性 :依赖
hashCode
方法和equals
方法保证键的唯一 - 初始化 :如果键要存储的是自定义对象,需要重写
hashCode
和equals
方法
代码示例
创建一个学生类,键是学生对象(Student):
java
import java.util.Objects;
public class Student {
private String mame;// 姓名
private int age;// 年龄
public Student() {
}
public Student(String mame, int age) {
this.mame = mame;
this.age = age;
}
public String getMame() {
return mame;
}
public void setMame(String mame) {
this.mame = mame;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 为了保证对象的唯一性,重写equals 和 hashCode
// 如果学生对象的成员变量值相同,我们就认为是同一个对象
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(mame, student.mame);
}
@Override
public int hashCode() {
return Objects.hash(mame, age);
}
}
测试类:
java
//创建HashMap集合对象
HashMap<Student, String> stu = new HashMap<>();
// 创建学生对象
Student s1 = new Student("李一", 30);
Student s2 = new Student("李一", 30);
Student s3 = new Student("张三", 37);
Student s4 = new Student("李四", 32);
// 把学生添加到集合
stu.put(s1,"北京");
stu.put(s2,"重庆");
stu.put(s3,"上海");
stu.put(s4,"曼哈顿");
// 遍历集合
for (Student key : stu.keySet()) {
String value = stu.get(key);
System.out.println(key.getName() + "," + key.getAge() + "," + value);
}
TreeMap集合
TreeMap
是Java集合框架中实现Map
接口的一个类,它使用红黑树(Red-Black Tree)作为底层数据结构,确保键的有序存储。
TreeMap
的键值对是有序的,可以按照键的自然顺序(键实现Comparable接口)或者通过自定义Comparator进行排序。- 红黑树是一种自平衡二叉查找树,保证了插入、删除和查找操作的时间复杂度在最坏情况下均为O(log n)。
代码示例
创建一个学生类:
java
import java.util.Objects;
public class Student implements Comparable<Student> {
private String name;// 姓名
private int age;// 年龄
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 为了保证对象的唯一性,重写equals 和 hashCode
// 如果学生对象的成员变量值相同,我们就认为是同一个对象
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
// 继承 Comparable接口,重写 compareTo方法,实现学生根据条件进行排序
@Override
public int compareTo(Student o) {
//按照年龄进行排序
int result = o.getAge() - this.getAge();
//次要条件,按照姓名排序。
result = result == 0 ? o.getName().compareTo(this.getName()) : result;
return result;
}
}
测试方法:
java
//创建TreeMap集合对象
TreeMap<Student, String> stu = new TreeMap<>();
// 创建学生对象
Student s1 = new Student("李一", 30);
Student s2 = new Student("李一", 30);
Student s3 = new Student("张三", 37);
Student s4 = new Student("李四", 32);
// 把学生添加到集合
stu.put(s1,"北京");
stu.put(s2,"重庆");
stu.put(s3,"上海");
stu.put(s4,"曼哈顿");
// 遍历集合
for (Student key : stu.keySet()) {
String value = stu.get(key);
System.out.println(key.getName() + "," + key.getAge() + "," + value);
}