在 Java 编程的世界里,集合框架是组织和操作数据的强大工具。其中,双列集合以独特的键值对存储方式,为我们处理数据提供了别样的思路。本文将深入探讨 Java 双列集合的核心概念、常见实现类及其应用场景。
双列集合的基本特性
双列集合,区别于单列集合,它一次存储一对数据,即键(Key)和值(Value)。这一特性使得数据的关联存储和快速检索成为可能。
- 键的唯一性:键在集合中不能重复,这是双列集合的基本约束。每个键如同一个独特的标识符,确保数据的准确性和高效查找。
- 值的多样性:与键不同,值可以重复。这为存储和管理复杂数据提供了灵活性,例如,一个键可以对应多个相同的值,或者不同键对应相同的值。
- 一一对应关系:键和值之间是严格的一一对应关系。通过键,我们可以迅速定位到其对应的值,这种映射关系是双列集合的核心机制。
- 键值对对象:在 Java 中,键和值的组合被称为 “键值对” 或 “Entry 对象”。它封装了键值之间的关系,是双列集合操作的基本单元。
Map 接口:双列集合的基石
Map
是双列集合的顶层接口,定义了一系列操作键值对的方法,所有具体的双列集合实现类都继承了这些方法。
常见 API 方法详解
V put(K key, V value)
:用于向集合中添加元素。如果键不存在,会将新的键值对添加到集合中,并返回null
;若键已存在,则会用新的值覆盖旧值,并返回被覆盖的值。V remove(Object key)
:根据指定的键删除对应的键值对元素,并返回被删除的值。如果键不存在,返回null
。void clear()
:移除集合中的所有键值对元素,使集合变为空集。boolean containKey(Object Key)
:判断集合是否包含指定的键,返回true
或false
,用于快速检查键的存在性。boolean containValue(Object value)
:判断集合是否包含指定的值,由于值可能重复,查找效率相对较低。boolean isEmpty()
:判断集合是否为空,即集合中是否不包含任何键值对。int size()
:返回集合中键值对的个数,即集合的长度。
import java.util.HashMap;
import java.util.Map;
public class MapAPIDemo {
public static void main(String[] args) {
// 创建Map集合的对象
Map<String, String> m = new HashMap<>();
// 添加元素
m.put("喜羊羊", "美羊羊");
m.put("智羊羊", "丽羊羊");
m.put("灰太狼", "红太狼");
// 演示put方法的覆盖功能
String value2 = m.put("喜羊羊", "暖羊羊");
System.out.println("被覆盖的值: " + value2);
// 删除元素
String result = m.remove("喜羊羊");
System.out.println("删除的值: " + result);
// 清空集合
m.clear();
System.out.println("清空后的集合: " + m);
// 判断是否包含键和值
boolean keyResult = m.containsKey("喜羊羊");
System.out.println("是否包含键'喜羊羊': " + keyResult);
boolean valueResult = m.containsValue("喜羊羊2");
System.out.println("是否包含值'喜羊羊2': " + valueResult);
// 判断集合是否为空
boolean isEmptyResult = m.isEmpty();
System.out.println("集合是否为空: " + isEmptyResult);
// 获取集合长度
int size = m.size();
System.out.println("集合长度: " + size);
}
}
Map 的遍历方式
遍历 Map 集合是日常编程中常见的操作,Java 提供了多种方式来实现这一需求。
键找值方式
- 获取键集:首先通过
keySet()
方法获取 Map 中所有键的集合,该集合是一个单列集合。 - 遍历键集:使用
for - each
循环、迭代器或forEach
方法遍历键集。 - 获取对应值:在遍历过程中,通过键调用
get(key)
方法获取对应的值。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapTraversalByKey {
public static void main(String[] args) {
// 创建Map集合的对象
Map<String, String> map = new HashMap<>();
map.put("喜羊羊", "美羊羊");
map.put("智羊羊", "丽羊羊");
map.put("灰太狼", "红太狼");
// 获取所有的键
Set<String> keys = map.keySet();
// 使用for - each循环遍历
for (String key : keys) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
System.out.println("____________________________");
// 使用迭代器遍历
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String next = it.next();
String value = map.get(next);
System.out.println(next + "=" + value);
}
System.out.println("____________________________");
// 使用forEach方法遍历
keys.forEach(s -> {
String value = map.get(s);
System.out.println(s + "=" + value);
});
}
}
键值对方式
- 获取键值对集:通过
entrySet()
方法获取 Map 中所有键值对的集合,每个元素都是一个Entry
对象。 - 遍历键值对集:同样可以使用
for - each
循环、迭代器或forEach
方法遍历。 - 获取键和值:从
Entry
对象中分别调用getKey()
和getValue()
方法获取键和值。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapTraversalByEntry {
public static void main(String[] args) {
// 创建Map集合的对象
Map<String, String> map = new HashMap<>();
map.put("喜羊羊", "美羊羊");
map.put("智羊羊", "丽羊羊");
map.put("灰太狼", "红太狼");
// 获取所有的键值对
Set<Map.Entry<String, String>> entries = map.entrySet();
// 使用for - each循环遍历
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
System.out.println("___________________________");
// 使用迭代器遍历
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
System.out.println(next);
}
System.out.println("--------------------------");
// 使用forEach方法遍历
entries.forEach(stringStringEntry -> System.out.println(stringStringEntry));
}
}
Lambda 表达式方式
Map
接口提供了forEach
方法,结合 Lambda 表达式可以简洁地遍历 Map。
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class MapTraversalWithLambda {
public static void main(String[] args) {
// 创建Map集合的对象
Map<String, String> map = new HashMap<>();
map.put("喜羊羊", "美羊羊");
map.put("智羊羊", "丽羊羊");
map.put("灰太狼", "红太狼");
// 使用Lambda表达式遍历
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key + "=" + value);
}
});
System.out.println("_______________________________");
// 使用更简洁的Lambda表达式
map.forEach((key, value) -> System.out.println(key + "=" + value));
}
}
常见的 Map 实现类
HashMap
- 基本特性:
HashMap
是Map
接口最常用的实现类,它基于哈希表结构实现。 - 性能特点:具有出色的插入和查找性能,时间复杂度通常为 O (1)。这得益于其哈希表的设计,通过哈希函数将键映射到特定的存储位置。
- 键的唯一性保证:依赖
hashCode
方法和equals
方法来保证键的唯一性。当向HashMap
中添加键值对时,首先根据键的hashCode
值计算存储位置,如果不同键的hashCode
值相同(即哈希冲突),则通过equals
方法进一步比较键的内容是否相同。如果键是自定义对象,必须重写hashCode
和equals
方法以确保正确的唯一性判断。 - 值的处理:对于值,
HashMap
不关心其唯一性,因此无需重写hashCode
和equals
方法(除非在特定业务逻辑中有此需求)。
LinkedHashMap
- 继承关系:
LinkedHashMap
继承自HashMap
,在保留哈希表特性的基础上,增加了对元素顺序的维护。 - 有序性实现:通过双向链表记录键值对的插入顺序,因此保证了存储和取出的元素顺序一致。这种特性在需要按插入顺序处理数据的场景中非常有用,例如缓存系统中需要按照访问顺序淘汰数据。
- 性能影响:由于额外维护了链表结构,
LinkedHashMap
的插入和删除操作性能略低于HashMap
,但查找性能基本相同。
TreeMap
- 底层结构:
TreeMap
基于红黑树结构实现,红黑树是一种自平衡的二叉搜索树。 - 排序特性:由键决定其特性,键不重复、无索引且可排序。默认情况下,
TreeMap
按照键的自然顺序(即实现了Comparable
接口的顺序)进行排序。也可以通过在创建集合时传递Comparator
比较器对象来自定义排序规则。 - 应用场景:适用于需要对键进行排序的场景,例如统计单词出现频率并按字母顺序输出。
自定义排序规则示例
- 实现 Comparable 接口:让自定义类实现
Comparable
接口,在compareTo
方法中定义比较规则。
import java.util.TreeMap;
class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Student other) {
// 按年龄升序排序
return this.age - other.age;
}
}
public class TreeMapSortByComparable {
public static void main(String[] args) {
TreeMap<Student, String> map = new TreeMap<>();
map.put(new Student("Alice", 20), "A");
map.put(new Student("Bob", 18), "B");
map.put(new Student("Charlie", 22), "C");
map.forEach((student, value) -> System.out.println(student.getName() + "(" + student.getAge() + ")=" + value));
}
}
- 使用 Comparator 比较器:创建
TreeMap
时传递Comparator
对象,在compare
方法中定义比较规则。
import java.util.Comparator;
import java.util.TreeMap;
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class TreeMapSortByComparator {
public static void main(String[] args) {
TreeMap<Student, String> map = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 按姓名降序排序
return s2.getName().compareTo(s1.getName());
}
});
map.put(new Student("Alice", 20), "A");
map.put(new Student("Bob", 18), "B");
map.put(new Student("Charlie", 22), "C");
map.forEach((student, value) -> System.out.println(student.getName() + "(" + student.getAge() + ")=" + value));
}
}
新的统计思想:利用 Map 进行数据统计
在实际编程中,经常需要统计数据出现的次数。利用Map
可以轻松实现这一功能。
统计字符出现次数示例
import java.util.StringJoiner;
import java.util.TreeMap;
public class CharacterCounting {
public static void main(String[] args) {
String s = "aababcabcdabcde";
TreeMap<Character, Integer> tm = new TreeMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (tm.containsKey(c)) {
int count = tm.get(c);
count++;
tm.put(c, count);
} else {
tm.put(c, 1);
}
}
System.out.println(tm);
StringBuilder sb = new StringBuilder();
tm.forEach((key, value) -> sb.append(key).append("(").append(value).append(")"));
System.out.println(sb);
StringJoiner sj = new StringJoiner("", "", "");
tm.forEach((key, value) -> sj.add(key + "").add("(").add(value + "").add(")"));
System.out.println(sj);
}
}
在这个例子中,我们使用TreeMap
来统计字符串中每个字符出现的次数。键表示字符,值表示出现的次数。如果不需要对结果进行排序,使用HashMap
会有更高的效率。
如何选择合适的双列集合
- 默认选择:如果没有特殊需求,
HashMap
通常是最佳选择,因为它具有最高的效率,适用于大多数需要快速插入和查找的场景。 - 有序性需求:当需要保证存储和取出的元素顺序一致时,应选择
LinkedHashMap
。例如,在实现一个需要按访问顺序缓存数据的系统中,LinkedHashMap
可以方便地实现这一功能。 - 排序需求:如果需要对键进行排序,无论是自然顺序还是自定义顺序,
TreeMap
都是首选。比如在统计单词频率并按字母顺序输出的场景中,TreeMap
能很好地满足需求。
通过深入理解双列集合的特性、常见实现类及其应用场景,我们能够在 Java 编程中更加高效、灵活地处理数据,提升程序的性能和可读性。希望本文能为你在集合框架的学习和应用中提供有价值的参考。
标签:Map,Java,map,System,println,双列,集合,out From: https://blog.csdn.net/2201_75813105/article/details/145240341