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

【算法训练营 · 专项练习篇】Stream流与函数式编程

文章目录

  • 函数式编程理论基础
  • 函数式编程实践题(12道)
    • 题目1:用Predicate组合过滤数字
    • 题目2:用Function组合转换数据
    • 题目3:用Consumer消费并处理数据
    • 题目4:用Supplier生成随机数列表
    • 题目5:静态方法引用转换字符串为整数
    • 题目6:实例方法引用(对象)处理字符串
    • 题目7:类的实例方法引用排序字符串
    • 题目8:构造方法引用创建对象列表
    • 题目9:自定义函数式接口实现加减乘除
    • 题目10:用Comparator排序对象(多条件)
    • 题目11:用lambda作为Runnable创建线程
    • 题目12:用Optional结合Function处理可能为null的值
  • Stream理论基础
  • 附:关于原始类型流
  • Stream编程实践题
    • 题目1:过滤偶数并求平方和
    • 题目2:字符串处理与排序
    • 题目3:对象分组统计
    • 题目4:求列表最大值
    • 题目5:嵌套列表去重合并
    • 题目6:字符串拼接
    • 题目7:统计可被3整除的数字
    • 题目8:筛选成绩前3名学生
    • 题目9:计算列表元素乘积
    • 题目10:数字分区(偶数/奇数)
    • 题目11:对象属性拼接
    • 题目12:使用Function接口转换
    • 题目13:计算正整数平均值

函数式编程理论基础

关于Stream流与函数式编程的详细理论知识,可以参考我的另一篇文章:【Java笔记】函数式编程,本篇文章旨在通过练习题的方式,加速实践中对Stream流与函数式编程的掌握程度。

这里只提几个注意点:

1. 函数式编程的核心思想

  • 函数是一等公民:函数可以作为参数传递、作为返回值返回,也可以赋值给变量。
  • 不可变性:数据一旦创建不可修改(如Stream操作不会改变源数据),避免副作用。
  • 纯函数:输入相同则输出一定相同,无对外界的依赖或修改(无副作用)。

2. Lambda表达式(Lambda表达式就是一个函数式接口的实例!)

  • 语法(参数列表) -> { 代码块 }
    • 若参数只有一个,可省略括号:s -> s.length()
    • 若代码块只有一行,可省略大括号和return(a, b) -> a + b

以下是Java中常用函数式接口的汇总表格,涵盖核心接口的定义、功能及使用场景,方便快速查阅和实践:

  1. 函数式接口的核心是仅包含一个抽象方法(可含默认方法/静态方法),因此可通过Lambda表达式或方法引用简化实现。
  2. 子接口(如UnaryOperator继承Function)的功能更具体,适用于输入输出类型一致的场景,可减少代码冗余。
  3. Comparator虽不在java.util.function包中,但因高频用于函数式编程(如Stream.sorted()),故纳入表格。
接口名称所在包抽象方法功能描述示例代码(Lambda/方法引用)常见用途
Predicate<T>java.util.functionboolean test(T t)接收一个参数,返回布尔值,用于条件判断n -> n % 2 == 0(判断偶数)
String::isEmpty
集合过滤(filter())、参数校验
Function<T, R>java.util.functionR apply(T t)接收一个参数T,返回结果R,用于数据转换s -> s.length()(字符串转长度)
Integer::parseInt
集合转换(map())、数据格式转换
Consumer<T>java.util.functionvoid accept(T t)接收一个参数,无返回值,用于消费数据(仅处理不返回)System.out::println(打印元素)
list::add
遍历处理(forEach())、资源消耗
Supplier<T>java.util.functionT get()无参数,返回T类型结果,用于提供数据(生成/获取数据)() -> new ArrayList<>()
Random::nextInt
数据生成(Stream.generate())、延迟加载
Comparator<T>java.utilint compare(T o1, T o2)接收两个参数,返回int(正负/0),用于比较排序(a, b) -> a - b(整数升序)
String::compareTo
集合排序(sorted())、自定义排序规则
UnaryOperator<T>java.util.functionT apply(T t)Function的子接口,输入和输出类型相同,用于一元运算x -> x * 2(翻倍)
String::toUpperCase
同类型数据转换(如自增、格式化)
BinaryOperator<T>java.util.functionT apply(T t1, T t2)BiFunction的子接口,输入两个同类型参数,返回同类型结果,用于二元运算(a, b) -> a + b(求和)
Math::max
聚合操作(reduce())、两数运算
BiPredicate<T, U>java.util.functionboolean test(T t, U u)接收两个参数,返回布尔值,用于多条件判断(a, b) -> a.contains(b)(字符串包含)多参数过滤、复合条件校验
BiFunction<T, U, R>java.util.functionR apply(T t, U u)接收两个参数(T和U),返回R,用于多参数转换(a, b) -> a + b(拼接字符串)
(k, v) -> new Entry(k, v)
多字段组合转换、键值对处理
BiConsumer<T, U>java.util.functionvoid accept(T t, U u)接收两个参数,无返回值,用于消费两个数据(k, v) -> System.out.println(k + ":" + v)遍历Map(forEach())、多参数处理

3. 方法引用(简化Lambda的“语法糖”)
当Lambda体仅调用一个已存在的方法时,可用方法引用替代,格式为类名/对象名::方法名

  • 4种类型
    1. 静态方法引用类名::静态方法(如Integer::parseInt
    2. 实例方法引用(对象)对象::实例方法(如str::toUpperCasestr是某个String对象)
    3. 实例方法引用(类)类名::实例方法(第一个参数是方法的调用者,如String::compareTo,等价于(s1, s2) -> s1.compareTo(s2)
    4. 构造方法引用类名::new(如ArrayList::new,等价于() -> new ArrayList<>()

4. 函数式接口的组合与扩展

  • 默认方法:函数式接口可通过默认方法提供组合能力(如Predicate.and()Function.andThen())。
    例:Predicate<Integer> isEven = n -> n%2==0;
    Predicate<Integer> isGreaterThan5 = n -> n>5;
    Predicate<Integer> combined = isEven.and(isGreaterThan5);(同时满足两个条件)

函数式编程实践题(12道)

题目1:用Predicate组合过滤数字

需求:给定整数列表,过滤出“大于5且为偶数”的数字,使用Predicate.and()组合条件。
知识点Predicate接口、and()组合、lambda表达式

答案

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;public class PredicateCombine {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(3, 6, 8, 10, 4, 12, 7);// 定义两个PredicatePredicate<Integer> isGreaterThan5 = n -> n > 5;Predicate<Integer> isEven = n -> n % 2 == 0;// 组合条件:大于5 且 是偶数List<Integer> result = numbers.stream().filter(isGreaterThan5.and(isEven)) // 组合Predicate.collect(Collectors.toList());System.out.println(result); // 输出:[6, 8, 10, 12]}
}

题目2:用Function组合转换数据

需求:给定字符串列表,先将字符串转为长度(Function<String, Integer>),再将长度翻倍(Function<Integer, Integer>),用andThen()组合两个Function。
知识点Function接口、andThen()组合

答案

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;public class FunctionCombine {public static void main(String[] args) {List<String> words = Arrays.asList("apple", "cat", "banana");// 第一个Function:字符串 -> 长度Function<String, Integer> strToLen = s -> s.length();// 第二个Function:长度 -> 翻倍Function<Integer, Integer> lenToDouble = len -> len * 2;// 组合:先转长度,再翻倍Function<String, Integer> combined = strToLen.andThen(lenToDouble);List<Integer> result = words.stream().map(combined) // 应用组合后的Function.collect(Collectors.toList());System.out.println(result); // 输出:[10, 6, 12]("apple"长度5→10,以此类推)}
}

题目3:用Consumer消费并处理数据

需求:给定整数列表,先用Consumer打印原始数字,再用Consumer计算平方并打印,用andThen()组合两个Consumer。
知识点Consumer接口、andThen()组合

答案

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;public class ConsumerCombine {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(2, 3, 4);// 第一个Consumer:打印原始数字Consumer<Integer> printOriginal = n -> System.out.print("原始:" + n + ";");// 第二个Consumer:打印平方Consumer<Integer> printSquare = n -> System.out.println("平方:" + n*n);// 组合:先打印原始,再打印平方Consumer<Integer> combined = printOriginal.andThen(printSquare);numbers.forEach(combined); // 遍历并应用组合后的Consumer// 输出:// 原始:2;平方:4// 原始:3;平方:9// 原始:4;平方:16}
}

题目4:用Supplier生成随机数列表

需求:用Supplier生成5个1-100的随机整数,收集为列表。
知识点Supplier接口、generate()生成Stream

答案

import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class SupplierDemo {public static void main(String[] args) {Random random = new Random();// Supplier:生成1-100的随机数Supplier<Integer> randomNumSupplier = () -> random.nextInt(100) + 1;// 生成5个随机数并收集为列表List<Integer> randomNumbers = Stream.generate(randomNumSupplier).limit(5) // 限制数量.collect(Collectors.toList());System.out.println("随机数列表:" + randomNumbers); // 例:[35, 72, 18, 91, 5]}
}

题目5:静态方法引用转换字符串为整数

需求:给定字符串列表(如["123", "45", "6"]),用Integer.parseInt的静态方法引用转换为整数列表。
知识点:静态方法引用(类名::静态方法

答案

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StaticMethodReference {public static void main(String[] args) {List<String> strNumbers = Arrays.asList("123", "45", "6", "78");// 静态方法引用:Integer::parseInt 等价于 s -> Integer.parseInt(s)List<Integer> numbers = strNumbers.stream().map(Integer::parseInt) // 用静态方法引用转换.collect(Collectors.toList());System.out.println(numbers); // 输出:[123, 45, 6, 78]}
}

题目6:实例方法引用(对象)处理字符串

需求:给定字符串列表,用某个String对象的contains方法(实例方法)过滤出包含特定字符的字符串(如包含"a")。
知识点:实例方法引用(对象::方法

答案

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class InstanceMethodRefObject {public static void main(String[] args) {List<String> words = Arrays.asList("apple", "banana", "cat", "date");String target = "a"; // 要包含的字符// 实例方法引用:target::contains 等价于 s -> target.contains(s)List<String> result = words.stream().filter(target::contains) // 过滤包含"a"的字符串.collect(Collectors.toList());System.out.println(result); // 输出:[]}
}

题目7:类的实例方法引用排序字符串

需求:用String::compareToIgnoreCase(类的实例方法引用)对字符串列表进行忽略大小写排序。
知识点:类的实例方法引用(类名::方法,第一个参数是调用者)

答案

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class InstanceMethodRefClass {public static void main(String[] args) {List<String> words = Arrays.asList("Banana", "apple", "Cherry", "date");// 类的实例方法引用:String::compareToIgnoreCase 等价于 (s1, s2) -> s1.compareToIgnoreCase(s2)List<String> sorted = words.stream().sorted(String::compareToIgnoreCase) // 忽略大小写排序.collect(Collectors.toList());System.out.println(sorted); // 输出:[apple, Banana, Cherry, date]}
}

题目8:构造方法引用创建对象列表

需求:给定字符串列表(如["张三", "李四"]),用Person::new(构造方法引用)创建Person对象列表(Personname属性的构造方法)。
知识点:构造方法引用(类名::new

答案

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;class Person {private String name;public Person(String name) { // 带name的构造方法this.name = name;}@Overridepublic String toString() {return "Person{name='" + name + "'}";}
}public class ConstructorRef {public static void main(String[] args) {List<String> names = Arrays.asList("张三", "李四", "王五");// 构造方法引用:Person::new 等价于 name -> new Person(name)List<Person> people = names.stream().map(Person::new) // 用构造方法创建对象.collect(Collectors.toList());System.out.println(people); // 输出:[Person{name='张三'}, Person{name='李四'}, Person{name='王五'}]}
}

题目9:自定义函数式接口实现加减乘除

需求:定义函数式接口Calculator(含抽象方法int calculate(int a, int b)),用lambda实现加、减、乘、除四种运算。
知识点:自定义函数式接口、lambda表达式实现

答案

// 自定义函数式接口(只有一个抽象方法)
@FunctionalInterface
interface Calculator {int calculate(int a, int b);
}public class CustomFunctionalInterface {public static void main(String[] args) {// 实现加法Calculator add = (a, b) -> a + b;// 实现减法Calculator subtract = (a, b) -> a - b;// 实现乘法Calculator multiply = (a, b) -> a * b;// 实现除法(简单处理,不考虑0)Calculator divide = (a, b) -> a / b;System.out.println("3+5=" + add.calculate(3, 5)); // 8System.out.println("10-4=" + subtract.calculate(10, 4)); // 6System.out.println("6*7=" + multiply.calculate(6, 7)); // 42System.out.println("20/5=" + divide.calculate(20, 5)); // 4}
}

题目10:用Comparator排序对象(多条件)

需求:定义Student类(nameagescore),对学生列表先按score降序排序,若分数相同则按age升序排序,用lambda实现Comparator
知识点Comparator接口、thenComparing()组合排序

答案

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;class Student {private String name;private int age;private int score;public Student(String name, int age, int score) {this.name = name;this.age = age;this.score = score;}public int getScore() { return score; }public int getAge() { return age; }@Overridepublic String toString() {return name + "(age:" + age + ", score:" + score + ")";}
}public class ComparatorDemo {public static void main(String[] args) {List<Student> students = Arrays.asList(new Student("张三", 18, 90),new Student("李四", 19, 85),new Student("王五", 17, 90), // 与张三分数相同,按年龄升序new Student("赵六", 20, 88));// 排序逻辑:先按score降序,再按age升序Comparator<Student> byScoreDesc = (s1, s2) -> Integer.compare(s2.getScore(), s1.getScore());Comparator<Student> byAgeAsc = Comparator.comparingInt(Student::getAge);List<Student> sorted = students.stream().sorted(byScoreDesc.thenComparing(byAgeAsc)) // 组合排序.toList();System.out.println(sorted); // 输出:// [王五(age:17, score:90), 张三(age:18, score:90), 赵六(age:20, score:88), 李四(age:19, score:85)]}
}

题目11:用lambda作为Runnable创建线程

需求:用lambda表达式实现Runnable接口,创建两个线程分别打印“线程1运行中”和“线程2运行中”。
知识点:lambda实现Runnable(函数式接口)

答案

public class LambdaRunnable {public static void main(String[] args) {// lambda实现Runnable接口的run()方法Runnable task1 = () -> System.out.println("线程1运行中");Runnable task2 = () -> System.out.println("线程2运行中");// 创建并启动线程new Thread(task1).start();new Thread(task2).start();// 输出(顺序可能交替):// 线程1运行中// 线程2运行中}
}

题目12:用Optional结合Function处理可能为null的值

需求:给定可能为null的字符串,若不为null则转换为大写并返回;若为null则返回默认值“EMPTY”。用OptionalFunction实现。
知识点Optionalmap()(接受Function)、orElse()

答案

import java.util.Optional;
import java.util.function.Function;public class OptionalWithFunction {public static void main(String[] args) {String str1 = "hello";String str2 = null;// Function:字符串 -> 大写Function<String, String> toUpper = String::toUpperCase;// 处理str1:不为null则转大写String result1 = Optional.ofNullable(str1).map(toUpper) // 应用Function.orElse("EMPTY");// 处理str2:为null则返回默认值String result2 = Optional.ofNullable(str2).map(toUpper).orElse("EMPTY");System.out.println(result1); // HELLOSystem.out.println(result2); // EMPTY}
}

Stream理论基础

关键特性说明:

  1. 创建操作:负责生成Stream,是流处理的起点,可从集合、数组、值、文件等多种来源生成。
  2. 中间操作:具有惰性执行特性(仅记录操作,不实际处理数据),且支持链式调用(返回Stream),最终需通过终止操作触发执行。
  3. 终止操作:触发流的实际处理(执行所有中间操作),返回具体结果(如集合、数值、布尔值等),且流一旦终止即不可再使用(否则抛出异常)。

一、Stream创建操作(生成Stream的方式)

操作类型方法示例功能描述适用场景示例代码
集合创建Collection.stream()从List/Set等Collection集合生成顺序流处理集合数据List<Integer> list = Arrays.asList(1,2,3); Stream<Integer> stream = list.stream();
集合创建(并行)Collection.parallelStream()从集合生成并行流(多线程处理,适合大数据量)高效处理大量数据list.parallelStream().forEach(System.out::println);
数组创建Arrays.stream(T[] array)从数组生成Stream处理数组数据int[] arr = {1,2,3}; IntStream stream = Arrays.stream(arr);
值序列创建Stream.of(T... values)直接通过多个值生成Stream(本质是数组转换)少量已知值的流处理Stream<String> stream = Stream.of("a", "b", "c");
空流创建Stream.empty()创建空Stream(避免null引发的空指针)作为默认返回值(如方法返回空流)Stream<String> emptyStream = Stream.empty();
无限流(生成)Stream.generate(Supplier<T>)通过Supplier生成无限长度的Stream(需配合limit限制长度)生成随机数、常量等无规律序列Stream<Integer> randoms = Stream.generate(() -> new Random().nextInt(10)).limit(5);
无限流(迭代)Stream.iterate(T seed, UnaryOperator<T>)从初始值seed开始,通过UnaryOperator迭代生成无限流(需配合limit)生成有规律的序列(如自增序列)Stream<Integer> nums = Stream.iterate(1, n -> n + 2).limit(3); // 1,3,5
文件流创建Files.lines(Path path)从文件路径生成Stream(每行作为一个元素)读取文件内容Stream<String> lines = Files.lines(Paths.get("test.txt"));

Stream中间操作(惰性执行,返回Stream,需终止操作触发)

操作类型方法示例功能描述关键参数/返回值示例代码(基于List<Integer> list = Arrays.asList(1,2,3,4,5,6);
过滤filter(Predicate<T> predicate)保留满足Predicate条件的元素参数:Predicate(判断逻辑)
返回:Stream
list.stream().filter(n -> n % 2 == 0) // 保留偶数:2,4,6
映射map(Function<T, R> mapper)将元素通过Function转换为另一种类型(1:1转换)参数:Function(转换逻辑)
返回:Stream
list.stream().map(n -> n * 2) // 元素翻倍:2,4,6,8,10,12
扁平化映射flatMap(Function<T, Stream<R>> mapper)将元素转换为Stream后“扁平化”(如List -> List)参数:Function(返回Stream)
返回:Stream
List<List<Integer>> nested = Arrays.asList(Arrays.asList(1,2), Arrays.asList(3,4)); nested.stream().flatMap(List::stream) // 1,2,3,4
排序sorted() / sorted(Comparator<T> comparator)自然排序(需元素实现Comparable)/ 自定义排序(Comparator)无参:自然排序
有参:Comparator
返回:Stream
list.stream().sorted((a,b) -> b - a) // 降序:6,5,4,3,2,1
去重distinct()基于equals()去重(保留第一个出现的元素)返回:StreamStream.of(1,2,2,3).distinct() // 1,2,3
限制数量limit(long maxSize)保留前maxSize个元素(若流长度小于maxSize则保留全部)参数:最大长度
返回:Stream
list.stream().limit(3) // 1,2,3
跳过元素skip(long n)跳过前n个元素(若流长度小于n则返回空流)参数:跳过数量
返回:Stream
list.stream().skip(2) // 3,4,5,6
peek(调试)peek(Consumer<T> action)对每个元素执行Consumer操作(不改变元素,用于调试打印)参数:Consumer(消费逻辑)
返回:Stream
list.stream().peek(n -> System.out.println("处理:" + n)).filter(n -> n>3) // 打印所有元素,保留4,5,6

Stream终止操作(触发流处理,返回非Stream结果)

操作类型方法示例功能描述返回值类型示例代码(基于List<Integer> list = Arrays.asList(1,2,3,4,5,6);
收集结果collect(Collector<T, A, R> collector)将流元素收集为集合/映射等(配合Collectors工具类)任意类型R(如List、Set、Map)list.stream().filter(n -> n>3).collect(Collectors.toList()) // [4,5,6]
计数count()返回流中元素的数量longlist.stream().filter(n -> n%2==0).count() // 3(2,4,6)
遍历forEach(Consumer<T> action)对每个元素执行Consumer操作(无返回值,终端操作)voidlist.stream().forEach(System.out::println) // 打印所有元素
匹配(任一)anyMatch(Predicate<T> predicate)判断是否存在至少一个元素满足Predicate(短路操作:找到即返回)booleanlist.stream().anyMatch(n -> n > 5) // true(6满足)
匹配(所有)allMatch(Predicate<T> predicate)判断是否所有元素都满足Predicate(短路操作:有一个不满足即返回)booleanlist.stream().allMatch(n -> n > 0) // true
匹配(无)noneMatch(Predicate<T> predicate)判断是否所有元素都不满足Predicate(短路操作:有一个满足即返回)booleanlist.stream().noneMatch(n -> n < 0) // true
查找第一个findFirst()返回流中第一个元素(对于有序流可靠,并行流中也能返回第一个)Optionallist.stream().filter(n -> n%2==0).findFirst() // Optional[2]
查找任意findAny()返回流中任意一个元素(并行流下可能更快,结果不确定)Optionallist.parallelStream().filter(n -> n>3).findAny() // 可能是4/5/6
最大值max(Comparator<T> comparator)根据Comparator返回流中最大元素Optionallist.stream().max(Integer::compare) // Optional[6]
最小值min(Comparator<T> comparator)根据Comparator返回流中最小元素Optionallist.stream().min(Integer::compare) // Optional[1]
归约(聚合)reduce(T identity, BinaryOperator<T> accumulator)从初始值identity开始,通过BinaryOperator聚合元素(如求和、乘积)T(非Optional,因有初始值)list.stream().reduce(0, Integer::sum) // 21(1+2+3+4+5+6)
归约(无初始值)reduce(BinaryOperator<T> accumulator)无初始值的聚合(若流为空返回空Optional)Optionallist.stream().reduce(Integer::sum) // Optional[21]

注意:

Collectors 的核心价值是:提供了一套开箱即用的“收集策略”,让你无需手动编写复杂逻辑,就能将 Stream 处理后的元素高效转换为所需的结果形式(集合、统计值、分组数据等)。

它是 Stream 终止操作的“左膀右臂”——几乎所有 stream.collect(...) 都依赖 Collectors 提供的收集器来完成最终的结果生成。

Collectors 提供了大量静态方法,返回各种“收集器”(Collector 接口的实现类),覆盖绝大多数常见的收集场景:

1. 收集到集合(最基础)
将流元素收集到 ListSetMap 等标准集合中。

  • toList():收集为 List(默认是 ArrayList
  • toSet():收集为 Set(默认是 HashSet
  • toMap():收集为 Map(需指定键和值的生成规则)

2. 聚合统计(计算数值结果)
对元素进行计数、求和、求平均值等聚合操作。

  • counting():统计元素数量(返回 Long
  • summingInt()/summingLong():对整数/长整型属性求和
  • averagingInt():求整数属性的平均值(返回 Double
  • maxBy()/minBy():根据比较器求最大/小元素

示例

List<Student> students = Arrays.asList(new Student("张三", 18, 90),new Student("李四", 19, 85)
);// 统计学生数量
Long count = students.stream().collect(Collectors.counting()); // 2// 求分数总和
Integer totalScore = students.stream().collect(Collectors.summingInt(Student::getScore)); // 90+85=175// 求分数平均值
Double avgScore = students.stream().collect(Collectors.averagingInt(Student::getScore)); // 175/2=87.5

3. 分组与分区(按规则拆分元素)

  • groupingBy():按指定规则(如属性)将元素分组,返回 Map<分组键, 元素列表>
  • partitioningBy():按布尔条件(Predicate)将元素分为两组,返回 Map<Boolean, 元素列表>(只有两个键:truefalse)。

示例

// 按年龄分组(groupingBy)
Map<Integer, List<Student>> groupByAge = students.stream().collect(Collectors.groupingBy(Student::getAge));// 按“分数是否≥90”分区(partitioningBy)
Map<Boolean, List<Student>> partitionByScore = students.stream().collect(Collectors.partitioningBy(s -> s.getScore() >= 90));
// 结果:{true=[张三], false=[李四]}

4. 字符串拼接
joining():将流中的字符串元素按指定分隔符拼接(支持前缀、后缀)。

示例

String joined = Stream.of("Java", "Stream", "Collectors").collect(Collectors.joining(", ")); // 结果:"Java, Stream, Collectors"// 带前缀和后缀
String withPrefixSuffix = Stream.of("a", "b").collect(Collectors.joining(", ", "{", "}")); // 结果:"{a, b}"

5. 转换元素后收集(mapping()
先对元素进行转换(如提取属性),再收集转换后的结果(常与 groupingBy 等组合使用)。

示例

// 按年龄分组后,只保留每组学生的姓名(先转换再收集)
Map<Integer, List<String>> ageToNames = students.stream().collect(Collectors.groupingBy(Student::getAge, // 分组键:年龄Collectors.mapping(Student::getName, // 转换:提取姓名Collectors.toList() // 收集为列表))); // 结果:{18=[张三], 19=[李四]}

附:关于原始类型流

Stream 中的 mapToIntflatMapToInt 等方法(还有类似的 mapToLong/flatMapToLongmapToDouble/flatMapToDouble)属于“映射到原始类型流”的中间操作。它们的核心作用是:将元素从引用类型(如 Integer)转换为基本类型(如 int),并返回对应的原始类型流(如 IntStream),从而避免自动装箱/拆箱的性能损耗,同时提供针对基本类型的特有操作(如求和、求平均等)。

核心特点

  • 输入:流中的元素(通常是引用类型,如 IntegerString 等)。
  • 输出:原始类型流(IntStream/LongStream/DoubleStream),而非普通的 Stream<T>
  • 优势:处理基本类型时更高效(无装箱拆箱开销),且原始类型流提供了专属的终止操作(如 sum()average())。

一、mapToInt:将元素转换为 int,返回 IntStream
方法签名

IntStream mapToInt(ToIntFunction<? super T> mapper)
  • ToIntFunction 是函数式接口,抽象方法为 int applyAsInt(T value):接收一个元素 T,返回一个 int

使用场景:当需要将流中的元素(如 IntegerString)转换为 int 类型,并进行数值计算时使用。

示例 1:将 Integer 流转为 IntStream 并求和

import java.util.Arrays;
import java.util.List;public class MapToIntDemo {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4);// 1. 用 mapToInt 将 Integer 转换为 int(避免装箱),得到 IntStream// 2. 用 IntStream 的 sum() 直接求和(普通 Stream 没有 sum() 方法)int sum = numbers.stream().mapToInt(Integer::intValue) // ToIntFunction:Integer -> int.sum(); // IntStream 特有方法:求和System.out.println("总和:" + sum); // 输出:10(1+2+3+4)}
}

示例 2:将字符串转为其长度的 int

List<String> words = Arrays.asList("apple", "banana", "cat");// 转换为字符串长度的 IntStream,求最大值
int maxLength = words.stream().mapToInt(String::length) // ToIntFunction:String -> 长度(int).max() // IntStream 特有:求最大值(返回 OptionalInt).orElse(0);System.out.println("最长字符串长度:" + maxLength); // 输出:6("banana"的长度)

二、flatMapToInt:将元素转换为 IntStream 并合并,返回单一 IntStream

方法签名

IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper)
  • mapper 是函数式接口:接收一个元素 T,返回一个 IntStream(子流)。
  • flatMapToInt 会将所有子流合并为一个单一的 IntStream(类似 flatMap 的“扁平化”逻辑,但针对 int 类型)。

使用场景:当流中的元素本身是“包含 int 的容器”(如 int[]List<Integer>),需要将这些容器中的 int 全部提取出来,合并成一个 IntStream 时使用。

示例 1:处理 int 数组的列表,提取所有 int 并求和

import java.util.Arrays;
import java.util.List;public class FlatMapToIntDemo {public static void main(String[] args) {// 流中的元素是 int 数组List<int[]> arrays = Arrays.asList(new int[]{1, 2},new int[]{3, 4, 5},new int[]{6});// 1. 用 flatMapToInt 将每个 int[] 转为 IntStream(Arrays::stream 可将数组转为流)// 2. 合并所有子流为一个 IntStream,再求和int total = arrays.stream().flatMapToInt(Arrays::stream) // mapper:int[] -> IntStream.sum();System.out.println("总和:" + total); // 输出:21(1+2+3+4+5+6)}
}

示例 2:将字符串列表拆分为字符的 ASCII 码流

List<String> words = Arrays.asList("ab", "cd");// 每个字符串拆分为字符,转换为 ASCII 码(int),合并为 IntStream
IntStream asciiStream = words.stream().flatMapToInt(s -> {// 将字符串的每个字符转为 ASCII 码(int),生成 IntStreamreturn s.chars(); // String 的 chars() 方法直接返回 IntStream});// 打印所有 ASCII 码(a=97, b=98, c=99, d=100)
asciiStream.forEach(System.out::println); // 输出:97 98 99 100

三、原始类型流(IntStream 等)的特有操作

IntStream/LongStream/DoubleStream 提供了普通 Stream 没有的终止操作,专为数值计算设计:

方法功能描述示例(基于 IntStream
sum()求所有元素的和int sum = intStream.sum();
average()求平均值(返回 OptionalDoubledouble avg = intStream.average().orElse(0);
max()/min()求最大/小值(返回 OptionalIntint max = intStream.max().orElse(-1);
count()统计元素数量(返回 longlong count = intStream.count();
toArray()转换为 int[] 数组int[] arr = intStream.toArray();

Stream编程实践题

以下是13道融合Stream流与函数式编程的Java编程题,涵盖过滤、转换、聚合、分组、排序等核心场景,每道题均附详细解题思路和代码实现:

题目1:过滤偶数并求平方和

需求:给定整数列表,过滤出所有偶数,计算这些偶数的平方和。
知识点filter()map()mapToInt()sum()

答案

import java.util.Arrays;
import java.util.List;public class EvenSquareSum {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);// 过滤偶数 -> 转平方 -> 求和int sum = numbers.stream().filter(n -> n % 2 == 0)  // 函数式接口Predicate:判断是否为偶数.map(n -> n * n)          // 函数式接口Function:转换为平方.mapToInt(Integer::intValue)  // 转换为IntStream便于求和.sum();System.out.println("偶数平方和:" + sum);  // 输出:4+16+36+64=120}
}

题目2:字符串处理与排序

需求:给定字符串列表,过滤出长度大于3的字符串,转换为大写后按自然顺序排序,最终收集为列表。
知识点filter()map()sorted()collect(Collectors.toList())

答案

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StringProcess {public static void main(String[] args) {List<String> words = Arrays.asList("apple", "cat", "banana", "dog", "grape");List<String> result = words.stream().filter(s -> s.length() > 3)  // 过滤长字符串.map(String::toUpperCase)     // 方法引用:转大写.sorted()                     // 自然排序(Comparable).collect(Collectors.toList()); // 收集结果System.out.println(result);  // 输出:[APPLE, BANANA, GRAPE]}
}

题目3:对象分组统计

需求:定义Student类(含nameage),给定学生列表,按年龄分组并统计每个年龄的学生数量。
知识点Collectors.groupingBy()、方法引用、Collectors.counting()

答案

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public int getAge() { return age; }
}public class StudentGroup {public static void main(String[] args) {List<Student> students = Arrays.asList(new Student("Alice", 18),new Student("Bob", 19),new Student("Charlie", 18),new Student("David", 19));// 按年龄分组,值为该年龄的人数Map<Integer, Long> ageCount = students.stream().collect(Collectors.groupingBy(Student::getAge,  // 分类函数(方法引用)Collectors.counting()  // 统计函数));System.out.println(ageCount);  // 输出:{18=2, 19=2}}
}

题目4:求列表最大值

需求:给定整数列表,使用Stream求最大值(若列表为空则返回-1)。
知识点max()ComparatororElse()

答案

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;public class FindMax {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(3, 7, 2, 9, 5);// List<Integer> numbers = Collections.emptyList(); // 测试空列表int max = numbers.stream().max(Comparator.naturalOrder())  // 自然排序比较器.orElse(-1);  // 空列表时返回默认值System.out.println("最大值:" + max);  // 输出:9(非空列表);-1(空列表)}
}

或者

import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
import java.util.stream.Collectors;public class FindMax {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(3, 7, 2, 9, 5);// List<Integer> numbers = Arrays.asList(); // 测试空列表// 1. 用Collectors.maxBy获取最大值(返回Optional<Integer>)// 2. 用orElse(-1)处理空列表情况int max = numbers.stream().collect(Collectors.maxBy(Comparator.comparingInt(Integer::intValue) // 补全比较器:比较Integer的int值)).orElse(-1); // 空列表时返回-1System.out.println("最大值:" + max); // 输出:9(非空列表);-1(空列表)}
}
  • Comparator.comparingInt(Integer::intValue):Collectors.maxBy 需要一个 Comparator 来定义比较规则。此处用 comparingInt 接收 ToIntFunction(通过 Integer::intValue 提取整数的原始值),实现对 Integer 元素的比较。
  • orElse(-1):Collectors.maxBy 返回 Optional(可能为空,如列表为空时)。orElse(-1) 表示:若 Optional 有值则取其值,否则返回默认值 -1,完美处理空列表场景。

题目5:嵌套列表去重合并

需求:给定列表的列表(List<List<Integer>>),提取所有元素并去重,最终收集为Set
知识点flatMap()distinct()collect(Collectors.toSet())

//flatMap 的参数本身就是一个函数式接口,它的作用就是定义 “如何将流中的每个元素转换为子流”。
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

flatMap 是 Stream 中用于扁平化流的中间操作,核心作用是:将流中每个元素转换为一个子流,然后把所有子流“合并”成一个单一的流(消除嵌套结构)。

一句话理解: 如果流中的元素本身是“容器”(如 ListSet 或其他流),flatMap 能把这些容器“拆开”,让容器里的元素直接成为新流的元素,实现从“嵌套流”到“单层流”的转换。

map 的对比(关键区别):

  • map:对每个元素做1:1转换(如把 String 转成 Integer),结果是“元素类型转换后的流”(如 Stream<String> → Stream<Integer>)。
  • flatMap:对每个元素做1:N转换(如把 List<Integer> 转成 Stream<Integer>),然后把所有子流合并,结果是“消除嵌套的单层流”(如 Stream<List<Integer>> → Stream<Integer>)。

答案

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;public class FlatMapDemo {public static void main(String[] args) {List<List<Integer>> nestedList = Arrays.asList(Arrays.asList(1, 2, 3),Arrays.asList(2, 3, 4),Arrays.asList(3, 4, 5));// 扁平化嵌套列表 -> 去重 -> 收集为SetSet<Integer> uniqueElements = nestedList.stream().flatMap(List::stream)  // 展平为Stream<Integer>.distinct()             // 去重.collect(Collectors.toSet());System.out.println(uniqueElements);  // 输出:[1, 2, 3, 4, 5]}
}

题目6:字符串拼接

需求:给定字符串列表,用逗号拼接所有元素(如["a", "b", "c"] -> "a,b,c")。
知识点Collectors.joining()

答案

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StringJoin {public static void main(String[] args) {List<String> parts = Arrays.asList("Java", "Stream", "Practice");String joined = parts.stream().collect(Collectors.joining(","));  // 按逗号拼接System.out.println(joined);  // 输出:Java,Stream,Practice}
}

题目7:统计可被3整除的数字

需求:给定整数列表,统计能被3整除的数字的个数。
知识点filter()count()

答案

import java.util.Arrays;
import java.util.List;public class CountDivisibleBy3 {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(3, 6, 9, 10, 12, 14);long count = numbers.stream().filter(n -> n % 3 == 0)  // 过滤可被3整除的数.count();  // 统计数量System.out.println("可被3整除的数有:" + count + "个");  // 输出:4个}
}

题目8:筛选成绩前3名学生

需求:定义Student类(含namescore),给定学生列表,筛选出成绩>80的学生,按成绩降序排序后取前3名的姓名。
知识点filter()sorted(Comparator)limit()map()

答案

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;class Student2 {private String name;private int score;public Student2(String name, int score) {this.name = name;this.score = score;}public String getName() { return name; }public int getScore() { return score; }
}public class Top3Students {public static void main(String[] args) {List<Student2> students = Arrays.asList(new Student2("Alice", 95),new Student2("Bob", 78),new Student2("Charlie", 88),new Student2("David", 92),new Student2("Eve", 85));List<String> top3Names = students.stream().filter(s -> s.getScore() > 80)  // 筛选及格学生.sorted(Comparator.comparingInt(Student2::getScore).reversed())  // 成绩降序.limit(3)  // 取前3名.map(Student2::getName)  // 提取姓名.collect(Collectors.toList());System.out.println("成绩前3名:" + top3Names);  // 输出:[Alice, David, Charlie]}
}

题目9:计算列表元素乘积

需求:给定整数列表,使用reduce()计算所有元素的乘积(空列表返回0)。
知识点reduce()orElse()

答案

import java.util.Arrays;
import java.util.Collections;
import java.util.List;public class ProductReduce {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(2, 3, 4, 5);// List<Integer> numbers = Collections.emptyList(); // 测试空列表int product = numbers.stream().reduce(1, (a, b) -> a * b);  // 初始值1,累积器:相乘// 等价于 .reduce(1, Integer::multiplyExact)// 处理空列表(reduce无初始值时返回Optional)int safeProduct = numbers.stream().reduce((a, b) -> a * b).orElse(0);System.out.println("乘积:" + product);       // 输出:120(非空)System.out.println("安全乘积:" + safeProduct);  // 输出:120(非空);0(空)}
}

题目10:数字分区(偶数/奇数)

需求:给定整数列表,将其分为“偶数”和“奇数”两个列表,用Map<Boolean, List<Integer>>存储(true对应偶数,false对应奇数)。
知识点Collectors.partitioningBy()

答案

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class NumberPartition {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);// 按是否为偶数分区Map<Boolean, List<Integer>> partition = numbers.stream().collect(Collectors.partitioningBy(n -> n % 2 == 0  // 分区条件));System.out.println("偶数:" + partition.get(true));   // 输出:[2,4,6,8]System.out.println("奇数:" + partition.get(false));  // 输出:[1,3,5,7]}
}

题目11:对象属性拼接

需求:定义Person类(含nameage),将List<Person>转换为List<String>,每个元素格式为“姓名:年龄”。
知识点map()、lambda表达式字符串拼接

答案

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() { return name; }public int getAge() { return age; }
}public class PersonToString {public static void main(String[] args) {List<Person> people = Arrays.asList(new Person("张三", 20),new Person("李四", 25),new Person("王五", 30));List<String> personInfo = people.stream().map(p -> p.getName() + ":" + p.getAge())  // 拼接属性.collect(Collectors.toList());System.out.println(personInfo);  // 输出:[张三:20, 李四:25, 王五:30]}
}

题目12:使用Function接口转换

需求:定义一个Function<String, Integer>接口实例,用于计算字符串长度,并用它转换字符串列表为长度列表。
知识点Function函数式接口、map()

答案

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;public class FunctionDemo {public static void main(String[] args) {List<String> words = Arrays.asList("apple", "banana", "cherry");// 定义Function:字符串 -> 长度Function<String, Integer> stringToLength = String::length;  // 方法引用// 应用Function转换List<Integer> lengths = words.stream().map(stringToLength)  // 用Function转换每个元素.collect(Collectors.toList());System.out.println("字符串长度:" + lengths);  // 输出:[5, 6, 6]}
}

题目13:计算正整数平均值

需求:给定整数列表,过滤掉负数后计算平均值(无正整数时返回0.0)。
知识点filter()mapToInt()average()orElse()

答案

import java.util.Arrays;
import java.util.List;public class PositiveAverage {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(-3, 5, -2, 8, 10);// List<Integer> numbers = Arrays.asList(-1, -2, -3); // 测试无正整数double average = numbers.stream().filter(n -> n > 0)  // 过滤正整数.mapToInt(Integer::intValue)  // 转IntStream.average()  // 计算平均值(返回OptionalDouble).orElse(0.0);  // 无元素时返回0.0System.out.println("正整数平均值:" + average);  // 输出:(5+8+10)/3=7.666...}
}
http://www.dtcms.com/a/568791.html

相关文章:

  • 泰州企业做网站百度地图怎么导航环线
  • int8_to_float(output_tensor->data.int8, output_float, load_class_num);
  • 使用Nmap扫描某个服务器所有开放端口
  • 如何看网站是用什么程序做的如何把qq音乐导入到wordpress
  • SpringCloud网关实战:路由与鉴权全解析
  • 基于ResNet50和PyTorch的猫狗图像分类系统设计与实现
  • 自回归模型例题(AR)与ACF/PACF图绘制
  • ESP32-WROOM-32E LED点灯系列
  • 《红色脉络:一部PLMN在中国的演进史诗 (1G-6G)》 第15篇 | 结语:无尽的前沿——PLMN的未来与中国的全球角色
  • 付网站开发费计入什么科目seo外包杭州
  • 外贸网站域名被封免费网络游戏大全
  • PySide6 Win10记事本从零到一——第七章 格式菜单界面与功能实现
  • PDF文件损坏打不开怎么修复?2025年最新修复工具测评与对比
  • 谈谈MYSQL索引失效场景
  • Qwen-Image-Edit本地到底如何部署使用?怎么还有comfyui
  • 佳能LBP6018L打印浅淡问题的尝试性解决方法
  • 微算法科技(NASDAQ MLGO):以隐私计算区块链筑牢多方安全计算(MPC)安全防线
  • SpringCache :让缓存开发更高效
  • 电路分析 | Phasor Analysis(篇 1)
  • 网站备案取消长春网站建设模板样式
  • get_ccopt系列命令介绍(二)
  • 成都工业学院文献检索在哪个网站做破解wordpress密码
  • 做网站用什么系统好网站登录验证码是怎么做的
  • SQL语法基础教程
  • 算法25.0
  • 无穿戴动捕技术:解锁动作捕捉新维度,拓展多元应用边界
  • 高速PCB设计指南(5)
  • 栈与队列---算法题
  • 外包加工网站开发一个网页具体流程
  • 泰安肥城做网站的公司平台推广活动策划方案