# Stream 流式编程

# 分类

分类图片

中间操作可分为:

  • 无状态:指元素的处理不受之前元素的影响
  • 有状态:指该操作只有拿到所有元素之后才能继续下去

终结操作可分为:

  • 非短路操作:指必须处理所有元素才能得到最终结果
  • 短路操作:指遇到某些符合条件的元素就可以得到最终结果

具体操作如图所示:


# Stream API 使用

# 流的构成

使用流的时候,通常包括三个步骤:

  1. 获取一个数据源
  2. 数据转换
    执行操作获取想要的结果,每次转换原有的 Stream 对象不变,返回一个新的 Stream 对象,这就允许对其操作可以像链条一样排列

# 流的创建

  • 通过 java.util.Collection.stream () 方法用集合创建流

    
      List<String> list = Arrays.asList("hello","world","stream");
      //创建顺序流
      Stream<String> stream = list.stream();
      //创建并行流
      Stream<String> parallelStream = list.parallelStream();
    
    
  • 使用 java.util.Arrays.stream (T [] array) 方法用数组创建流

    
      String[] array = {"h", "e", "l", "l", "o"};
      Stream<String> arrayStream = Arrays.stream(array);
    
    
  • Stream 的静态方法:of ()、iterate ()、generate ()

    
      Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6);
      
      Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(3);
      
      Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
    
    
  • stream 和 parallelStream 的简单区分

stream 是顺序流,由主线程按顺序对流执行操作,而 parallelStream 是并行流,内部以多线程并行执行的方式对流进行操作,需要注意使用并行流的前提是流中的数据处理没有顺序要求(会乱序,即使用了 forEachOrdered)


# 一、Stream 中间操作

Stream流中间操作是指在流链当中,可以对数据进行处理操作,包括filter过滤、map映射转换、flatMap合并、distinct去重、sorted排序等操作,这些操作都会返回一个新的Stream流对象,可以通过链式调用多个中间操作进行复杂的数据处理。

需要注意的是,中间操作需要具有终止操作才会触发。

# 无状态 (Stateless) 操作

# filter: 过滤出符合条件的元素

filter () 方法常用于实现数据过滤,即可以对集合、数组等数据源筛选出符合指定条件的元素,并返回一个新的流

// 将数组转换为一个字符串列表
List<String> numbers = Arrays.asList"13378520000","13278520000","13178520000","13358520000");
// 通过stream()方法创建一个流,接着使用filter方法过滤出前缀为"133"的元素,最终通过collect()方法将结果收集到一个新的列表中
List<String> filterNumbers = numbers.stream().filter(s -> s.startWith("133")).collect(Collectors.toList());
System.out.println(filterNumbers);

打印结果:[13378520000, 13358520000]

# map: 映射转换元素

map 方法用于对流中的每个元素进行映射操作,将其转换为另一个元素或者提取其中的信息,并返回一个新的流

1.2.1 转换元素

获取每个手机号的前七位子字符串

List<String> numbers = Arrays.asList("13378520000","13278520000","13178520000","13558520000");
//通过stream()方法创建一个流,使用map()方法将每个字符串转换为截取前7位的字符,最后使用collect()方法将结果收集到一个新列表中
List<String> filterdNumbers = numbers.stream().map(s -> s.substring(0,7)).collect(Collectors.toList());
System.out.println(filterdNumbers);


打印结果:[1337852, 1327852, 1317852, 1355852]


1.2.2 提取元素信息

获取每个对象的手机号

List<People> peopleList = Arrays.asList(
        new People("王二","13378520000"),
        new People("李二","13278520000"),
        new People("张四","13178520000")
);
//通过stream()方法创建一个流,使用map()方法提取每个用户的手机号,最后使用collect()方法将结果收集到一个新列表中
List<String> tel = peopleList.stream().map(People::getTel).collect(Collectors.toList());
System.out.println(tel);


打印结果:[13378520000, 13278520000, 13178520000]

# flatMap: 将多个流合并为一个流

1.3.1 实现多对多的映射

将 A 组数据和 B 组数据一次进行相加


List<Integer> listA = Arrays.asList(1,2,3);
List<Integer> listB = Arrays.asList(4, 5, 6);
List<Integer> list = listA.stream().flatMap(a -> listB.stream().map(b -> a +b)).collect(Collectors.toList());
System.out.println(list);


打印结果:  [5, 6, 7, 6, 7, 8, 7, 8, 9]

1.3.2 将多个列表合并成一个列表


List<List<String>> listOfLists = Arrays.asList(
        Arrays.asList("13378520000", "13278520000"),
        Arrays.asList("13178520000", "13558520000"),
        Arrays.asList("15138510000", "15228310000")
);
List<String> flatMapList = listOfLists.stream().flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(flatMapList);


打印结果:[13378520000, 13278520000, 13178520000, 13558520000, 15138510000, 15228310000]


# peek: 查看每个元素的信息,但不改变流中元素的状态

List<String> telList = Arrays.asList("13378520000","13278520000","13178520000","13558520000");
telList.stream().peek(t -> System.out.println(t))
        .map(t -> t.substring(0,3))
        .peek(t -> System.out.println(t))
        .collect(Collectors.toList());


重点: peek VS map

peek 操作一般用于 不想改变流中元素本身的类型或者只想元素的内部状态改变时
map 则用于改变流中元素本身,即从元素中派生出另一种类型的操作


# mapToInt、mapToLong、mapToDouble、flatMapToDouble、flatMapToInt、flatMapToLong

以上操作是 map 和 flatMap 的特别版,也就是针对特定的数据类型进行映射处理

其对应方法如下:


IntStream mapToInt(ToIntFunction<? super T> mapper);
 
LongStream mapToLong(ToLongFunction<? super T> mapper); 
 
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
 
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
 
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
 
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);


# 有状态 (Stateful) 操作

# distinct: 去除重复的元素

distinct () 方法可以用来去除流中的重复元素,生成无重复的列表
需要注意的是:distinct 用于针对流元素去重操作时,需要确定流中的元素实现了 equals () 和 hasCode () 方法


List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");
List<String> disNumbers = numbers.stream().distinct().collect(Collectors.toList());
System.out.println(disNumbers);		


打印结果:[13378520000, 15138510000, 13178520000]


# 1.5 sorted: 排序元素

sorted 方法用于对流中元素进行排序

1.5.1 升序排序


List<People> peopleList = Arrays.asList(
        new People("王二",20),
        new People("李二",30),
        new People("张四",31)
);
List<People> newpeopleList=peopleList.stream().sorted(Comparator.comparing(People::getAge)).collect(Collectors.toList());
//打印结果
newpeopleList.stream().forEach(System.out::println);


打印结果:
People{name='王二', age=20}
People{name='李二', age=30}
People{name='张四', age=31}

1.5.2 降序排序

通过 reversed () 方法进行逆序排序


List<People> peopleList = Arrays.asList(
        new People("王二",20),
        new People("李二",30),
        new People("张四",31)
);
List<People> newpeopleList = peopleList.stream().sorted(Comparator.comparing(People::getAge).reversed()).collect(Collectors.toList());
//打印结果
newpeopleList.stream().forEach(System.out::println);

打印结果:
People{name='张四', age=31}
People{name='李二', age=30}
People{name='王二', age=20}


# 1.7 limit 和 skip: 截取流中的部分元素

limit 返回一个包含前 n 个元素的新流
skip 返回一个丢弃前 n 个元素后剩余元素组成的新流


int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.print("取数组前5个元素:");
Arrays.stream(arr).limit(5).forEach(n -> System.out.print(n + " ")); // 输出结果为:1 2 3 4 5

System.out.print("跳过前3个元素,取剩余数组元素:");
Arrays.stream(arr).skip(3).forEach(n -> System.out.print(n + " ")); // 输出结果为:4 5 6 7 8 9 10


# 终结操作 (Terminal Operation)

# 短路 (Short-circuiting) 操作

  • anyMatch: Stream 中只要有一个元素符合传入的 predicate, 返回 true

boolean anyMatch(Predicate<? super T> predicate);

  • allMatch: Stream 中全部元素符合传入的 predicate, 返回 true

boolean allMatch(Predicate<? super T> predicate);

  • noneMatch: Stream 中没有一个元素符合传入的 predicate, 返回 true

boolean noneMatch(Predicate<? super T> predicate);

  • findFirst: 用于返回满足条件的第一个元素 (凡是该元素是封装在 Optional 类中)

Optional findFirst();

  • findAny: 返回流中的任意元素 (但是该元素也是封装在 Optional 类中)

Optional findAny();


# 非短路操作

# forEach()

该方法接收一个 lambda 表达式,然后在 Stream 的每一个元素上执行该表达式

void forEach(Consumer<? super T> action);


# forEachOrdered()

该方法接收一个 Lambda 表达式,然后按顺序在 Stream 中的每一个元素上执行该表达式

void forEachOrdered(Consumer<? super T> action);

该功能其实和 forEach 是很相似的,也是循环操作!那唯一的区别,就在于 forEachOrdered 是可以保证循环时元素是按原来的顺序逐个循环的!


# toArray()

返回包含此流元素的数组,当有参数时,

Object [] toArray();

A[] toArray(IntFunction<A[]> generator);

举例:



List<String> strList = Arrays.asList( "Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
 
Object [] strAryNoArg = strList.stream().toArray();
String [] strAry = strList.stream().toArray(String[]::new);