Stream流

一 为什么要用Stream流#

1.1 传统的方式,遍历,过滤集合中的数据#

Copy Highlighter-hljs
//创建一个List集合,存储姓名 List<String> list = new ArrayList<>(); list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰"); //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中 List<String> listA = new ArrayList<>(); for(String s : list){ if(s.startsWith("张")){ listA.add(s); } } //对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中 List<String> listB = new ArrayList<>(); for (String s : listA) { if(s.length()==3){ listB.add(s); } } //遍历listB集合 for (String s : listB) { System.out.println(s); } }

这段代码中含有三个循环,每一个作用不同:

    1. 首先筛选所有姓张的人;
    1. 然后筛选名字有三个字的人;
    1. 最后进行对结果进行打印输出。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使用另一个循环从头开始。

1.2 Stream流的方式,遍历,过滤集合中的数据#

Copy Highlighter-hljs
//创建一个List集合,存储姓名 List<String> list = new ArrayList<>(); list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰"); //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中 //对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中 //遍历listB集合 list.stream() .filter(name->name.startsWith("张")) .filter(name->name.length()==3) .forEach(name-> System.out.println(name));

1.3 流式思想概述#

  • 流式思想类似于工厂车间的“生产流水线

元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。 数据源流的来源。 可以是集合,数组等当使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。

二 Stream流的基础使用#

2.1 两种获取Stream流的方式#

所有的 Collection 集合都可以通过 stream 默认方法获取流;
Stream 接口的静态方法 of 可以获取数组对应的流。

Copy Highlighter-hljs
/* java.util.stream.Stream<T>是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。) 获取一个流非常简单,有以下几种常用的方式: - 所有的Collection集合都可以通过stream默认方法获取流; default Stream<E> stream​() - Stream接口的静态方法of可以获取数组对应的流。 static <T> Stream<T> of​(T... values) 参数是一个可变参数,那么我们就可以传递一个数组 */ public class Demo01GetStream { public static void main(String[] args) { //把集合转换为Stream流 List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); Set<String> set = new HashSet<>(); Stream<String> stream2 = set.stream(); Map<String,String> map = new HashMap<>(); //获取键,存储到一个Set集合中 Set<String> keySet = map.keySet(); Stream<String> stream3 = keySet.stream(); //获取值,存储到一个Collection集合中 Collection<String> values = map.values(); Stream<String> stream4 = values.stream(); //获取键值对(键与值的映射关系 entrySet) Set<Map.Entry<String, String>> entries = map.entrySet(); Stream<Map.Entry<String, String>> stream5 = entries.stream(); //把数组转换为Stream流 Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5); //可变参数可以传递数组 Integer[] arr = {1,2,3,4,5}; Stream<Integer> stream7 = Stream.of(arr); String[] arr2 = {"a","bb","ccc"}; Stream<String> stream8 = Stream.of(arr2); } }

2.2 Stream流中的常用方法_forEach#

forEach()#

虽然名字叫forEach(),但和for循环中的for-each 不同

Copy Highlighter-hljs
void forEach(Consumer<? super T> action);

Consumer接口#

java.util.function.Consumer 接口是一个函数型接口, 消费一个数据, 其数据类型由泛型决定。

Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。

2.3 Stream流中的常用方法_filter#

可以通过 filter 方法将一个流转换成另一个子集流

Copy Highlighter-hljs
Stream<T> filter(Predicate<? super T> predicate);

该接口接受一个Predicate函数式接口参数作为筛选条件

Predicate接口:
java.util.function.Predicate接口
作用:对某种数据类型的数据进行判断,结果返回一个boolean

抽象方法:test:
Predicate接口中包含了一个抽象方法
boolean test(T t):用来对指定数据类型数据进行判断的方法
结果;
符合条件,返回true
不符合条件,返回false

eg:

Copy Highlighter-hljs
Integer[] arr = {1,2,3,4,5,6,7,8,9,10}; Stream<Integer> stream1 = Stream.of(arr); // stream1.forEach(x -> System.out.println(x)); // stream 流使用过一次后即废弃 stream1.filter(x -> x > 5).forEach(x -> System.out.println(x));

2.4 Stream流的特点_只能使用一次#

  • stream流的注意事项, 只能使用一次

    若使用使用 已使用过的stream流,会在抛出如下错误
    IllegalStateException: stream has already been operated upon or closed

三 Stream流中的常用方法#

3.1 map#

  • map: 将一个流的数据映射到另一个流
Copy Highlighter-hljs
<R> Stream<R> map(Function<? super T, ? extends R> mapper); //eg Integer[] arr = {1,2,3,4,5,6,7,8,9,10}; Stream<Integer> stream1 = Stream.of(arr); Stream<String> stream2 = stream1.map(x -> String.valueOf(x));

3.2 count#

  • count : 统计流中元素的个数

3.3 limit#

  • limit: 方法可以对流进行截取,只取用前n个
Copy Highlighter-hljs
Stream<T> limit(long maxSize);

3.4 skip#

  • skip: 返回跳过前n个元素的流
Copy Highlighter-hljs
Stream<T> skip(long n);

3.5 concat#

  • concat: 静态方法 合并两个流
Copy Highlighter-hljs
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) { Objects.requireNonNull(a); Objects.requireNonNull(b); @SuppressWarnings("unchecked") Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>( (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator()); Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel()); return stream.onClose(Streams.composedClose(a, b)); }

四 集合元素处理#

Copy Highlighter-hljs
/* 练习:集合元素处理(传统方式) 现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤: 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。 5. 将两个队伍合并为一个队伍;存储到一个新集合中。 6. 根据姓名创建Person对象;存储到一个新集合中。 7. 打印整个队伍的Person对象信息。 */ //第一支队伍 ArrayList<String> one = new ArrayList<>(); one.add("迪丽热巴"); one.add("宋远桥"); one.add("苏星河"); one.add("石破天"); one.add("石中玉"); one.add("老子"); one.add("庄子"); one.add("洪七公"); //1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。 //2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。 Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3); //第二支队伍 ArrayList<String> two = new ArrayList<>(); two.add("古力娜扎"); two.add("张无忌"); two.add("赵丽颖"); two.add("张三丰"); two.add("尼古拉斯赵四"); two.add("张天爱"); two.add("张二狗"); //3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。 //4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。 Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2); //5. 将两个队伍合并为一个队伍;存储到一个新集合中。 //6. 根据姓名创建Person对象;存储到一个新集合中。 //7. 打印整个队伍的Person对象信息。 Stream.concat(oneStream,twoStream).map(name->new Person(name)).forEach(p-> System.out.println(p));

五 方法引用#

5.1 方法引用简介#

在使用 Lambda 表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作。

如果我们在 Lambda 中所指定的操作方案,已经有地方存在相同方案,则没有必要再写重复逻辑。

那又是如何使用已经存在的方案的呢?通过方法引用来使用已经存在的方案。

5.2 方法引用符号#

:: 该符号为引用运算符,而它所在的表达式被称为方法引用。

使用 Lambda,那么根据 “ 可推导就是可省略 ” 的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导。

使用方法引用,也是同样可以根据上下文进行推导,它是 Lambda 的孪生兄弟。

5.3 引用类的静态方法#

格式:类名::方法名

注意:Lambda 表达式被类方法替代时,它的形参全部传递给静态方法作为参数。

示例: 将 String 类型数字转为 int 类型数字。

Copy Highlighter-hljs
public interface StringConvert { int convertInt(String s); }

测试:

Copy Highlighter-hljs
public class Test { public static void main(String[] args) { // lambda方式 useStringConvert(s -> Integer.parseInt(s)); // 方法引用:引用类的静态方法,形参全部传递给静态方法作为参数 useStringConvert(Integer::parseInt); } public static void useStringConvert(StringConvert sc) { int number = sc.convertInt("156"); System.out.println("number = " + number); } }

运行:

Copy Highlighter-hljs
num = 156 num = 156

5.4 引用类的构造方法#

格式:类名::new

注意:Lambda 表达式被构造器替代时,它的形参全部传递给构造器作为参数。

示例:创建学生。

Copy Highlighter-hljs
public class Student { String name; int age; // 省略无参|有参构造方法、get|set方法、toString方法 } public interface StudentBuilder { Student builder(String name, int age); }

测试:

Copy Highlighter-hljs
public class Test { public static void main(String[] args) { // lambda方式 useStudentBuilder((name, age) -> new Student(name, age)); // 方法引用:引用类的构造方法,形参全部传递给构造器作为参数 useStudentBuilder(Student::new); } public static void useStudentBuilder(StudentBuilder sb) { Student student = sb.builder("张三", 23); System.out.println("student = " + student); } }

运行:

Copy Highlighter-hljs
student = Student{name='张三', age=23} student = Student{name='张三', age=23}

5.5 引用类的实例方法#

格式:类名::成员方法

注意:Lambda 表达式被类的实例方法替代时,第一个参数作为方法调用者,后面其余参数全部传递给该方法作为参数

示例:截取字符串,返回一个子串。

Copy Highlighter-hljs
public interface StringMethod { String mySubString(String string, int begin, int end); }

测试:

Copy Highlighter-hljs
public class Test { public static void main(String[] args) { // lambda方式 useStringMethod((string, begin, end) -> string.substring(begin, end)); // 方法引用:引用类的实例方法(成员方法),第一个参数作为方法调用者,后面其余参数全部传递给该方法作为参数 useStringMethod(String::substring); } public static void useStringMethod(StringMethod sm) { String string = sm.mySubString("HelloWord!", 3, 6); System.out.println("string = " + string); } }

运行:

Copy Highlighter-hljs
string = loW string = loW

5.6 引用对象的实例方法#

格式:对象::成员方法

说明:引用对象的实例方法就是引用类中的成员方法

注意:Lambda 表达式被对象的实例方法替代时,它的形参全部传递给该方法作为参数

示例:

Copy Highlighter-hljs
public interface Printer { void printUpper(String s); } public class PrintString { void printUpperCase(String s) { String string = s.toUpperCase(); System.out.println("string = " + string); } }

测试:

Copy Highlighter-hljs
public class Test { public static void main(String[] args) { // lambda方式 usePrinter(s -> System.out.println(s.toUpperCase())); // 方法引用:引用类对象的实例方法(成员方法),形参全部传递给该方法作为参数 PrintString ps = new PrintString(); usePrinter(ps::printUpperCase); } public static void usePrinter(Printer p) { p.printUpper("HelloWord!"); } }

运行:

Copy Highlighter-hljs
HELLOWORD! string = HELLOWORD!
posted @   Firewooood  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示
CONTENTS