当前位置: 首页 > news >正文

Java集合Map与Stream流:Map实现类特点、遍历方式、Stream流操作及Collections工具类方法

Java集合Map与Stream流:包含Map实现类特点、遍历方式、Stream流操作及Collections工具类方法

    • 1. 认识Map以及实现类的特点
      • 1.1 Map的核心概念
      • 1.2 三大实现类的特点对比
      • 1.3 代码案例:三种Map的基本使用对比
    • 2. Map提供的常用方法
    • 3. 三种遍历方式
      • 3.1 方式一:键找值(通过`keySet()`遍历键,再获取值)
      • 3.2 方式二:键值对(通过`entrySet()`遍历,推荐)
      • 3.3 方式三:Lambda表达式(JDK8+,简洁)
    • 4. HashMap、LinkedHashMap、TreeMap的常见应用场景案例
      • 4.1 HashMap:无需顺序的键值对存储(最常用)
      • 4.2 LinkedHashMap:需要保持插入顺序的场景
      • 4.3 TreeMap:需要排序的场景
    • 5. 认识Stream流
      • 5.1 什么是Stream流?
      • 5.2 作用与优势
      • 5.3 处理数据的步骤
      • 5.4 数据处理方式与结果应用
    • 6. 获取Stream流的几种方式
      • 6.1 从集合获取流
      • 6.2 从数组获取流
      • 6.3 通过`Stream.of()`方法获取流
    • 7. Stream常用中间方法及代码案例
      • 7.1 `filter(Predicate<T> predicate)`:过滤元素
      • 7.2 `sorted()`/`sorted(Comparator<T> comparator)`:排序
      • 7.3 `distinct()`:去重
      • 7.4 `map(Function<T, R> mapper)`:转换元素类型
      • 7.5 `limit(long maxSize)`:限制元素数量
      • 7.6 `skip(long n)`:跳过元素
    • 8. Stream常用终结方法及代码案例
      • 8.1 `forEach(Consumer<T> action)`:遍历元素
      • 8.2 `count()`:统计元素数量
      • 8.3 `collect(Collector<T, A, R> collector)`:收集为集合
      • 8.4 `max(Comparator<T> comparator)`/`min(Comparator<T> comparator)`:最大/小值
      • 8.5 `anyMatch(Predicate<T> predicate)`:判断是否有元素满足条件
    • 9. Collections工具类操作集合的常用方法
      • 9.1 排序与反转
      • 9.2 查找与替换
      • 9.3 集合拷贝与同步化
      • 9.4 可变参数方法(重点强调)
      • 9.5 其他常用方法

1. 认识Map以及实现类的特点

1.1 Map的核心概念

Map是Java集合框架中用于存储键值对(Key-Value) 的接口,与Collection(单列集合)不同,Map是双列集合,其核心特点是:

  • 键(Key)唯一:不允许重复,若添加重复键,新值会覆盖旧值
  • 值(Value)可重复:不同键可对应相同值
  • 键值对映射:通过键快速查找值,类似于现实中的“字典”(键=单词,值=释义)

1.2 三大实现类的特点对比

实现类底层结构有序性排序性线程安全性能(增删查)核心特点
HashMap哈希表(数组+链表+红黑树)无序(键值对存储顺序与插入顺序无关)无(需手动排序)高(O(1)平均)JDK8后当链表长度>8时转为红黑树,兼顾数组查询快和链表增删快的优势
LinkedHashMap哈希表+双向链表有序(保持插入顺序)略低于HashMap通过双向链表记录插入顺序,适合需要“按添加顺序访问”的场景(如历史记录)
TreeMap红黑树(自平衡二叉树)有序(按键自然排序或定制排序)有(键需可比较)中(O(log n))键必须实现Comparable接口或传入Comparator,适合需要“排序键值对”的场景

1.3 代码案例:三种Map的基本使用对比

import java.util.*;public class MapDemo {public static void main(String[] args) {// 1. HashMap:无序Map<String, Integer> hashMap = new HashMap<>();hashMap.put("苹果", 5);hashMap.put("香蕉", 3);hashMap.put("橙子", 4);System.out.println("HashMap(无序):" + hashMap); // 输出顺序可能为 {香蕉=3, 苹果=5, 橙子=4}// 2. LinkedHashMap:保持插入顺序Map<String, Integer> linkedHashMap = new LinkedHashMap<>();linkedHashMap.put("苹果", 5);linkedHashMap.put("香蕉", 3);linkedHashMap.put("橙子", 4);System.out.println("LinkedHashMap(插入顺序):" + linkedHashMap); // 输出 {苹果=5, 香蕉=3, 橙子=4}// 3. TreeMap:按键自然排序(String默认按字典序)Map<String, Integer> treeMap = new TreeMap<>();treeMap.put("苹果", 5);treeMap.put("香蕉", 3);treeMap.put("橙子", 4);System.out.println("TreeMap(自然排序):" + treeMap); // 输出 {苹果=5, 橙子=4, 香蕉=3}(字典序:苹<橙<香)}
}

2. Map提供的常用方法

Map接口定义了操作键值对的核心方法,以下是最常用的API及案例(以HashMap为例):

方法名作用代码案例(基于Map<String, Integer> map = new HashMap<>()
put(K key, V value)添加/修改键值对(键存在则覆盖值)map.put("苹果", 5);(添加);map.put("苹果", 6);(修改为6)
get(Object key)根据键获取值(键不存在返回nullInteger count = map.get("苹果");(返回5)
remove(Object key)根据键删除键值对,返回被删除的值Integer removed = map.remove("苹果");(返回5,map中移除"苹果")
containsKey(Object key)判断是否包含指定键boolean hasApple = map.containsKey("苹果");(返回true)
containsValue(Object value)判断是否包含指定值boolean has5 = map.containsValue(5);(返回true)
size()返回键值对数量int size = map.size();(返回当前键值对总数)
isEmpty()判断是否为空(键值对数量为0)boolean empty = map.isEmpty();(返回false,因已添加数据)
clear()清空所有键值对map.clear();(map变为空)
keySet()返回所有键的集合(Set<K>Set<String> keys = map.keySet();(获取所有水果名称)
values()返回所有值的集合(Collection<V>Collection<Integer> values = map.values();(获取所有数量)
entrySet()返回键值对集合(Set<Map.Entry<K,V>>Set<Map.Entry<String, Integer>> entries = map.entrySet();

3. 三种遍历方式

3.1 方式一:键找值(通过keySet()遍历键,再获取值)

原理:先获取所有键的集合(keySet()),再遍历键集合,通过get(key)获取对应值。
适用场景:只需遍历键或通过键获取值的场景。

Map<String, Integer> fruitMap = new HashMap<>();
fruitMap.put("苹果", 5);
fruitMap.put("香蕉", 3);
fruitMap.put("橙子", 4);// 1. 获取所有键的集合
Set<String> keys = fruitMap.keySet();
// 2. 遍历键集合(增强for循环)
for (String key : keys) {// 3. 通过键获取值Integer value = fruitMap.get(key);System.out.println(key + ":" + value); // 输出:苹果:5,香蕉:3,橙子:4(顺序不确定)
}

3.2 方式二:键值对(通过entrySet()遍历,推荐)

原理entrySet()将每个键值对封装为Map.Entry<K,V>对象(Entry是Map的内部接口,包含getKey()getValue()方法),直接遍历Entry对象即可同时获取键和值,性能优于键找值(无需多次调用get(key))。

// 1. 获取键值对集合(每个元素是Entry对象)
Set<Map.Entry<String, Integer>> entries = fruitMap.entrySet();
// 2. 遍历Entry集合(迭代器方式)
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while (iterator.hasNext()) {Map.Entry<String, Integer> entry = iterator.next();String key = entry.getKey(); // 获取键Integer value = entry.getValue(); // 获取值System.out.println(key + ":" + value);
}

3.3 方式三:Lambda表达式(JDK8+,简洁)

原理:通过forEach(BiConsumer<? super K, ? super V> action)方法,直接传入Lambda表达式遍历键值对,最简洁。

// Lambda表达式遍历:(key, value) -> 处理逻辑
fruitMap.forEach((key, value) -> {System.out.println(key + ":" + value);
});

4. HashMap、LinkedHashMap、TreeMap的常见应用场景案例

4.1 HashMap:无需顺序的键值对存储(最常用)

场景:存储用户信息(键=用户ID,值=用户对象),无需关心顺序,追求查询效率。

// 存储学生信息:键=学号(唯一),值=学生对象
Map<String, Student> studentMap = new HashMap<>();
studentMap.put("2023001", new Student("张三", 18));
studentMap.put("2023002", new Student("李四", 19));
// 快速查询学号为2023001的学生
Student zhangsan = studentMap.get("2023001");

4.2 LinkedHashMap:需要保持插入顺序的场景

场景:记录用户操作历史(按操作顺序展示),如浏览器的“历史记录”(先访问的网站在前)。

// 存储浏览历史:键=URL,值=访问时间,保持插入顺序
Map<String, String> history = new LinkedHashMap<>();
history.put("https://www.baidu.com", "2023-10-01 08:00");
history.put("https://www.google.com", "2023-10-01 09:00");
history.put("https://www.github.com", "2023-10-01 10:00");
// 遍历输出时,顺序与插入顺序一致(百度→谷歌→GitHub)
history.forEach((url, time) -> System.out.println(time + ":" + url));

4.3 TreeMap:需要排序的场景

场景:商品价格排行榜(按价格升序/降序排列),或字典(按字母顺序排列单词)。

// 存储商品价格:键=商品名,值=价格,按价格升序排序(默认自然排序,String按字典序,Integer按数字序)
Map<String, Double> productPrices = new TreeMap<>();
productPrices.put("笔记本", 4999.9);
productPrices.put("手机", 2999.9);
productPrices.put("平板", 1999.9);
// 遍历输出时,键按字典序排列:平板→手机→笔记本(价格1999.9→2999.9→4999.9)
productPrices.forEach((product, price) -> System.out.println(product + ":" + price + "元"));// 若需按价格降序,可传入自定义比较器:
Map<String, Double> sortedByPriceDesc = new TreeMap<>((a, b) -> productPrices.get(b).compareTo(productPrices.get(a)) // 降序:后减前
);
sortedByPriceDesc.putAll(productPrices); // 此时顺序为:笔记本→手机→平板

5. 认识Stream流

5.1 什么是Stream流?

Stream流是Java 8引入的处理集合数据的高级工具,它不是数据结构,而是数据处理管道,可以对集合、数组等数据源进行高效的聚合操作(如过滤、排序、统计等)。

5.2 作用与优势

  • 简化代码:用链式调用替代传统的for循环,代码更简洁易读
  • 函数式编程:支持Lambda表达式,专注“做什么”而非“怎么做”
  • 惰性执行:中间操作仅记录操作逻辑,不立即执行,直到调用终结操作才触发处理
  • 并行处理:可轻松切换为并行流(parallelStream()),利用多核CPU提高处理效率

5.3 处理数据的步骤

Stream流处理分为3步

  1. 获取流:从数据源(集合、数组等)创建Stream流
  2. 中间操作:对数据进行处理(过滤、排序、转换等),返回新的Stream流(可链式调用多个中间操作)
  3. 终结操作:触发流处理,返回结果(如遍历、统计、收集为集合),流一旦终结则不可再使用

5.4 数据处理方式与结果应用

  • 数据处理方式(中间操作)

    • 过滤(filter):保留满足条件的元素
    • 排序(sorted):对元素排序(自然排序或定制排序)
    • 去重(distinct):去除重复元素(依赖equals()hashCode()
    • 转换(map):将元素转换为另一种类型(如将字符串转为长度)
    • 限制(limit(n)):保留前n个元素
    • 跳过(skip(n)):跳过前n个元素
  • 结果应用方式(终结操作)

    • 遍历(forEach):逐个处理元素(无返回值)
    • 统计(count):返回元素数量;max/min:返回最大/小值
    • 收集(collect):将结果收集为集合(如ListSetMap
    • 匹配(anyMatch/allMatch/noneMatch):判断是否满足条件
    • 查找(findFirst/findAny):返回第一个/任意元素

6. 获取Stream流的几种方式

6.1 从集合获取流

  • List/Set集合:直接调用stream()parallelStream()(并行流)
  • Map集合:需先获取键集、值集或键值对集,再调用stream()
// List集合
List<String> list = Arrays.asList("苹果", "香蕉", "橙子");
Stream<String> listStream = list.stream();// Map集合(键流、值流、键值对流)
Map<String, Integer> map = new HashMap<>();
map.put("苹果", 5);
map.put("香蕉", 3);
Stream<String> keyStream = map.keySet().stream(); // 键流(水果名)
Stream<Integer> valueStream = map.values().stream(); // 值流(数量)
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream(); // 键值对流

6.2 从数组获取流

通过Arrays.stream(数组)获取流:

String[] fruits = {"苹果", "香蕉", "橙子"};
Stream<String> arrayStream = Arrays.stream(fruits);int[] numbers = {1, 2, 3, 4};
IntStream intStream = Arrays.stream(numbers); // 基本类型数组对应IntStream/LongStream等

6.3 通过Stream.of()方法获取流

Stream.of(T... values)可直接传入多个参数或数组创建流:

// 多个参数
Stream<String> singleParamStream = Stream.of("苹果"); // 单一参数
Stream<String> multiParamStream = Stream.of("苹果", "香蕉", "橙子"); // 多个参数// 数组(注意:传入数组时,若为引用类型数组,流元素为数组本身;需用Arrays.stream()获取数组元素流)
String[] arr = {"苹果", "香蕉"};
Stream<String[]> arrayAsElementStream = Stream.of(arr); // 流元素是String[]数组
Stream<String> arrayElementsStream = Arrays.stream(arr); // 流元素是数组中的字符串(正确方式)

7. Stream常用中间方法及代码案例

中间操作返回新的Stream流,可链式调用,常用方法及案例如下(以List<Integer> numbers = Arrays.asList(3, 1, 2, 3, 4, 5)为例):

7.1 filter(Predicate<T> predicate):过滤元素

保留满足条件的元素(Predicate是函数式接口,接收T返回boolean)。

// 过滤出偶数(保留2、4)
Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0); // n为流中的每个元素,返回true则保留

7.2 sorted()/sorted(Comparator<T> comparator):排序

  • sorted():自然排序(元素需实现Comparable接口)
  • sorted(comparator):定制排序(传入比较器)
// 自然排序(1, 2, 3, 3, 4, 5)
Stream<Integer> sortedNatural = numbers.stream().sorted();// 降序排序(5, 4, 3, 3, 2, 1)
Stream<Integer> sortedDesc = numbers.stream().sorted((a, b) -> b - a); // 定制比较器:后减前为降序

7.3 distinct():去重

去除重复元素(依赖元素的equals()hashCode()方法)。

// 去重后:3, 1, 2, 4, 5(原集合中的两个3保留一个)
Stream<Integer> distinctNumbers = numbers.stream().distinct();

7.4 map(Function<T, R> mapper):转换元素类型

将元素转换为另一种类型(Function接口:接收T,返回R)。

// 将整数转换为字符串("3", "1", "2", "3", "4", "5")
Stream<String> numberStrings = numbers.stream().map(n -> "数字:" + n); // 每个元素转为"数字:n"格式

7.5 limit(long maxSize):限制元素数量

保留前maxSize个元素。

// 保留前3个元素:3, 1, 2
Stream<Integer> limited = numbers.stream().limit(3);

7.6 skip(long n):跳过元素

跳过前n个元素,保留剩余元素。

// 跳过前2个元素:2, 3, 4, 5
Stream<Integer> skipped = numbers.stream().skip(2);

8. Stream常用终结方法及代码案例

终结操作触发流处理并返回结果,常用方法及案例如下(数据源同上:List<Integer> numbers = Arrays.asList(3, 1, 2, 3, 4, 5)):

8.1 forEach(Consumer<T> action):遍历元素

逐个处理元素(Consumer接口:接收T,无返回值)。

// 遍历输出每个元素
numbers.stream().forEach(n -> System.out.print(n + " ")); // 输出:3 1 2 3 4 5 

8.2 count():统计元素数量

返回流中元素的个数(long类型)。

// 统计偶数个数(2、4 → 共2个)
long evenCount = numbers.stream().filter(n -> n % 2 == 0).count();
System.out.println("偶数个数:" + evenCount); // 输出:2

8.3 collect(Collector<T, A, R> collector):收集为集合

将流元素收集为ListSetMap等,需配合Collectors工具类。

import java.util.stream.Collectors;// 收集为List(去重后的偶数:2, 4)
List<Integer> evenList = numbers.stream().filter(n -> n % 2 == 0).distinct().collect(Collectors.toList());// 收集为Set(自动去重:3, 1, 2, 4, 5)
Set<Integer> numberSet = numbers.stream().collect(Collectors.toSet());

8.4 max(Comparator<T> comparator)/min(Comparator<T> comparator):最大/小值

返回流中最大或最小的元素(Optional<T>类型,避免空指针)。

// 求最大值(5)
Optional<Integer> maxNum = numbers.stream().max(Integer::compare); // 自然排序比较器(等价于(a,b)->a.compareTo(b))
System.out.println("最大值:" + maxNum.get()); // 输出:5// 求最小值(1)
Optional<Integer> minNum = numbers.stream().min(Integer::compare);
System.out.println("最小值:" + minNum.get()); // 输出:1

8.5 anyMatch(Predicate<T> predicate):判断是否有元素满足条件

返回boolean,只要有一个元素满足条件则返回true。

// 判断是否存在大于4的元素(5>4 → true)
boolean hasGreaterThan4 = numbers.stream().anyMatch(n -> n > 4);
System.out.println("是否存在大于4的元素:" + hasGreaterThan4); // 输出:true

9. Collections工具类操作集合的常用方法

java.util.Collections是操作集合的工具类,提供了大量静态方法,以下是常用方法及案例:

9.1 排序与反转

  • sort(List<T> list):对List进行自然排序(元素需实现Comparable
  • sort(List<T> list, Comparator<T> c):定制排序
  • reverse(List<?> list):反转List中元素的顺序
List<Integer> nums = new ArrayList<>(Arrays.asList(3, 1, 2));
Collections.sort(nums); // 自然排序:[1, 2, 3]
Collections.reverse(nums); // 反转:[3, 2, 1]
Collections.sort(nums, (a, b) -> b - a); // 定制降序:[3, 2, 1](与反转结果一致)

9.2 查找与替换

  • max(Collection<? extends T> coll)/min(Collection<? extends T> coll):返回最大/小元素
  • fill(List<? super T> list, T obj):用指定元素填充List(替换所有元素)
  • replaceAll(List<T> list, T oldVal, T newVal):替换List中所有旧值为新值
List<String> fruits = new ArrayList<>(Arrays.asList("苹果", "香蕉", "苹果"));
String maxFruit = Collections.max(fruits); // 自然排序最大值(按字典序:香蕉)
Collections.fill(fruits, "橙子"); // 填充后:[橙子, 橙子, 橙子]
Collections.replaceAll(fruits, "橙子", "葡萄"); // 替换后:[葡萄, 葡萄, 葡萄]

9.3 集合拷贝与同步化

  • copy(List<? super T> dest, List<? extends T> src):将src中的元素拷贝到dest(需保证dest.size() ≥ src.size())
  • synchronizedList(List<T> list):将非线程安全的List转为线程安全的List(同理有synchronizedSetsynchronizedMap
List<Integer> src = Arrays.asList(1, 2, 3);
List<Integer> dest = new ArrayList<>(Arrays.asList(0, 0, 0, 0)); // dest容量≥src(3个元素)
Collections.copy(dest, src); // dest变为:[1, 2, 3, 0](前3个元素被src覆盖)// 同步化集合(多线程环境下使用)
List<String> syncFruits = Collections.synchronizedList(new ArrayList<>());
syncFruits.add("苹果"); // 线程安全的添加操作

9.4 可变参数方法(重点强调)

CollectionsaddAllnCopies等方法支持可变参数T... elements),可变参数本质是数组的简化写法,允许传入任意数量的参数(0个或多个)。

  • addAll(Collection<? super T> c, T... elements):向集合中添加多个元素
  • nCopies(int n, T o):返回包含n个相同元素的不可变List
List<String> list = new ArrayList<>();
// 可变参数:传入多个元素(苹果、香蕉、橙子),等价于传入数组new String[]{"苹果", "香蕉", "橙子"}
Collections.addAll(list, "苹果", "香蕉", "橙子"); // list变为:[苹果, 香蕉, 橙子]// nCopies:创建包含3个"重复"的不可变List
List<String> repeated = Collections.nCopies(3, "重复"); // [重复, 重复, 重复]

可变参数注意事项

  • 一个方法只能有一个可变参数,且必须是最后一个参数
  • 调用时可传入数组,也可直接传入多个参数(编译器自动转为数组)

9.5 其他常用方法

  • shuffle(List<?> list):随机打乱List元素顺序(如洗牌)
  • frequency(Collection<?> c, Object o):统计元素在集合中出现的次数
List<Integer> cards = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Collections.shuffle(cards); // 随机打乱顺序(如[3, 1, 5, 2, 4])
int count = Collections.frequency(cards, 3); // 统计3出现的次数(1次)
http://www.lryc.cn/news/621969.html

相关文章:

  • Transformer实战(11)——从零开始构建GPT模型
  • 【入门级-算法-6、排序算法:排序的基本概念冒泡排序】
  • 【100页PPT】数字化转型某著名企业集团信息化顶层规划方案(附下载方式)
  • Redis入门和简介
  • LeetCode 刷题【42. 接雨水】
  • 基于51单片机声控灯设计 智能声音+光线控制 楼道灯 声控开关
  • RabbitMQ面试精讲 Day 23:分布式事务与可靠投递
  • 【Redis】分布式系统的演化过程
  • [Oracle数据库] Oracle 常用函数
  • 接口芯片断电高阻态特性研究与应用分析
  • 基于 ArcFace/ArcMargin 损失函数的深度特征学习高性能人脸识别解决方案
  • 解释器模式C++
  • EN 61547照明产品的电磁兼容抗干扰标准
  • 图数据库如何构筑 Web3 风控防线 聚焦批量注册与链上盗转 悦数图数据库
  • eBPF技术介绍
  • 【Java】HashMap的详细介绍
  • YAML:锚点深度解析,告别重复,拥抱优雅的配置艺术
  • 【Java Web 快速入门】十、AOP
  • 「 CentOS7 安装部署k8s」
  • 水环境遥感分析!R语言编程+多源遥感数据预处理;水体指数计算、水深回归分析、水温SVM预测、水质神经网络建模及科研级可视化制图
  • 关于simplifyweibo_4_moods数据集的分类问题
  • 云原生俱乐部-k8s知识点归纳(3)
  • 2025年中国AI算力基础设施发展趋势洞察
  • MySQL 全面指南:从入门到精通——深入解析安装、配置、操作与优化
  • Linux 进程、线程与 exec/系统调用详解
  • 力扣top100(day04-06)--贪心算法
  • 自动处理考勤表——如何使用Power Query,步步为营,一点点探索自定义函数
  • 陪伴,是挫折教育最暖的底色
  • Java 中使用阿里云日志服务(SLS)完整指南
  • Hologres实战:路径分析函数