Stream流实例

需求:

  1. 1. 创建一个集合,存储多个字符串元素
  2. 1. 把集合中所有以"张"开头的元素存储到一个新的集合中
  3. 1. "张"开头的集合中的长度为3的元素存储到一个新的集合中
  4. 1. 遍历上一步的集合

代码实现:(原有方法)
public class StreamDemo {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<>();
al.add(“张三”);
al.add(“张大四”);
al.add(“张小六”);
al.add(“李三”);
al.add(“大四三”);
al.add(“小六”);
al.add(“三三”);
al.add(“张张”);
ArrayList<String> al1 = new ArrayList<>();
for (String s : al) {
/使用String.split分割符切分字符串获取索引,判断第一个字符是否等于张
if (s.split(“”)[0].equals(“张”)){
al1.add(s);
System.out.println(s); }
/
// 简化可以使用String的startsWith()方法判断第一个字符是什么
if (s.startsWith(“张”)) {
al1.add(s); } }
ArrayList<String> al2 = new ArrayList<>();
for (String s : al1) {
if (s.length() == 3) {
al2.add(s);
System.out.println(s); } } }}————————————————————-
// 消除冗余代码使用stream()流改进
// 调用stream()中的filter()方法(传递一个函数式Predicate判断接口)
// 函数式接口都是可以使用Lambda表达式的
// 在调用forEach()方法(传递一个函数式Consumer消费接口)执行消费语句
list.
stream()
.filter(s -> s.startsWith(“张”)) // filter 过滤首字符串是否为张
.
filter(s -> s.length()==3) // filter判断字符串长度是否为3
.
forEach(System.out::println)**; // 使用方法引用改进
使用Stream流的方式完成过滤操作

  1. - xx.stream().filter(Predicate判断函数式接口).forEach(Consumer消费函数式接口);
  2. - Stream可以很好的提升代码的阅读性,可以完美展示无关逻辑方式的语义: 生成流、过滤、打印
  3. - Stream是真正的将函数式编程风格引入到Java
  4. - <br />

Stream流的生成方式

image.png

Stream流的使用过程

生成流

  • 通过数据源(集合,数组等)生成流
  • 例如:list.stream()

    中间操作流

  • 一个Stream流一旦生成后,可以跟随多个中间操作,其目的就是讲生成的流打开,然后对打开流的数据进行操作(过滤/映射),然后返回一个新的流,交给下一个操作使用

  • 例如:filter()过滤方法

    终结操作流

  • 一个流只能有一个终结操作,当这个操作执行后,流就被关闭了,无法再操作. 所以Stream流的最后一个操作一定是forEach()


  • Stream流的生成方式

    Stream流的常见生成方式

  • Collection体系的集合可以使用默认方法stream()生成流

    • default Stream stream()
  • Map体系的集合间接的生成流
  • 数组可以通过Stream接口的静态方法of(T…values)生成流
    • T…values)T是一个泛型兼容所有数据类型,…values是一个可变数组,可以接收多个数组

// Collection体系的集合可以使用默认方法stream()生成流
// Collection的子类单列排序list无需排序有重复和set无序无重复

List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();

// Map体系的无法直接生成流,只能通过Map中的方法间接的生成流
Map<String,Integer> map = new HashMap<>();
// 通过Map的KeySet()方法获取键的流
Stream<String> keyStream = map.keySet().stream();
// 通过Map的values()方法获取值的流
Stream<Integer> valueStream = map.values().stream();
// 通过Map的entrySet()方法获取整个Map的键值对流
Stream<Map.Entry<String,Integer>> mapStream = map.entrySet().stream();

// 数组可以通过Stream接口的静态方法of(T…values)生成流
String[] strArray = {“hello”,”world”,”java”};
Stream<String> strStream = Stream.of(strArray);
// 因为(…values)是一个可变参数,可以直接传递数组
Stream<String> stringStream = Stream.of(“hello”, “world”, “java”);
Stream<Integer> intStream = Stream.of(1, 2, 3);

Stream流的常见中间操作方法— filter

filter

Stream filter(Predicate<? super T> predicate)
返回由与此给定谓词匹配的此流的元素组成的流。用于对流中的数据进行过滤

Predicate函数式接口参数

boolean test(T t)
在给定的参数上评估这个谓词。

代码实现:
ArrayList<String> list = new ArrayList<>();
list.add(“张三”);
list.add(“张大四”);
list.add(“张小六”);
list.add(“李三”);
list.add(“大四三”);
list.add(“小六”);
list.add(“三三”);
list.add(“张张”);
// 将张开头的元素输出
list.stream().filter(s -> s.startsWith(“张”)).forEach(System.out::println);
System.out.println(“———————————————-“);
// 长度为3的元素输出
list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
System.out.println(“———————————————-“);
// 张开头并且长度为3的元素输出
list.stream().filter(s -> s.startsWith(“张”)).filter(s -> s.length() == 3).forEach(System.out::println);

Stream流的常见中间操作方法— limit&skip

limit

—- Stream limit(long maxSize)
— 返回由该流的元素组成的流,截断长度不能超过maxSize 。
- 返回流中元素组成的流,截取前指定参数个数的数据

skip

—- Stream skip(long n)
— 在丢弃流的第一个n元素后,返回由该流的n元素组成的流。如果此流包含少于n元素,那么将返回一个空流。
- 跳过指定参数个数的数据,返回由该流的剩余元素组成的流
ArrayList<String> list = new ArrayList<>();
list.add(“张三”);
list.add(“张大四”);
list.add(“张小六”);
list.add(“李三”);
list.add(“大四三”);
list.add(“小六”);
list.add(“三三”);
list.add(“张张”);
// 取前三个元素输出
list.stream().limit(3).forEach(System.out::println);
// 张三 张大四 张小六
System.out.println(“—————————-“);
// 跳过三个元素,将剩下的元素输出
list.stream().skip(3).forEach(System.out::println);
// 李三 大四三 小六 三三 张张
System.out.println(“—————————-“);
// 跳过两个元素,把剩下的前两个元素输出
list.stream().skip(2).limit(2).forEach(System.out::println);
// 张小六 张三

Stream流的常见中间操作方法— concat&distinct

distinct

Stream distinct()
返回由该流的不同元素(根据Object.equals(Object) )组成的流。

concat

static Stream concat(Stream<? extends T> a,Stream<? extends T> b)
合并Stream a流和Stream b流为一个流。
创建一个懒惰连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。如果两个输入流都被排序,则生成的流被排序,并且如果任何一个输入流是并行的,则并行。当结果流关闭时,调用两个输入流的关闭处理程序。

// 取前4个元素组成一个流,创建对象获取这个流
Stream<String> limit = list.stream().limit(4);
// 跳过2个元素组成一个流,创建对象获取这个流
Stream<String> skip = list.stream().skip(2);
// Stream concat(Stream<? extends T> a,Stream<? extends T> b)
// 合并Stream a流和Stream b流为一个流,最后使用forEach()将两个流关闭。
// Stream.concat(limit, skip).forEach(System.out::println);
// 合并上面两个流,输出结果并保证结果元素不重复

Stream.concat(limit,skip).distinct().forEach(System.out::println);

Stream流的常见中间操作方法— sorted

sorted()

Stream sorted()
返回由此流的元素组成的流,根据自然顺序排序。

sorted(Comparator)

Stream sorted(Comparator<? super T> comparator)
返回由该流的元素组成的流,根据提供的Comparator进行排序。
ArrayList<String> list = new ArrayList<>();
list.add(“liuxiaoxia”);
list.add(“zhangxiaowu”);
list.add(“yangshufen”);
list.add(“lidazui”);
list.add(“daweiwang”);
list.add(“xiaoliuzi”);
list.add(“sansanniang”);
list.add(“ererniang”);
// 按照字母顺序把数据在控制台输出
list.stream().sorted().forEach(System.out::println);
System.out.println(“—————————“);
// 按照字母长度把数据在控制台输出
list.stream().sorted((s1,s2)-> s1.length()-s2.length()).forEach(System.out::println);
// 方法引用简化Lambda使用Comparator的comparingInt()方法比较长度
list.stream().sorted(Comparator.comparingInt(String::length)).forEach(System.out::println);
// 比较长度后并没有在对顺序排序
list.stream().sorted((s1,s2)->{
int i = s1.length() - s2.length();
int i1 = i == 0 ? s1.compareTo(s2) : i;
return i1;
}).forEach(System.out::println);

Stream流的常见中间操作方法— map&mapToInt

map

Stream map(Function<? super T,? extends R> mapper)
返回由给定函数应用于此流的元素的结果组成的流。

mapToInt

IntStream mapToInt(ToIntFunction<? super T> mapper)
返回一个IntStream ,其中包含将给定函数应用于此流的元素的结果。
IntStream : 表示原始int流
ToIntFunction接口中的方法 int applyAsInt(T value)
ArrayList<String> list = new ArrayList<>();
list.add(“10”);
list.add(“20”);
list.add(“30”);
list.add(“40”);
list.add(“50”);
list.add(“60”);
list.add(“70”);
list.add(“80”);
// 将集合中的字符串转换为整数后输出,使用map()方法
list.stream().map(Integer::parseInt).forEach(System.out::println);
// 将集合中的字符串转换为整数后输出,使用mapToInt()方法
list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
// 可以通过mapToInt的intStream原始int流中的sum()返回此流中元素的总和。
int sum = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(sum); // 360

Stream流的常见中间操作方法— forEach&count

Stream流的常见终结操作方法

forEach

  1. - void forEach(Consumer<? super T> action) 对此流的每个元素执行操作。

count

  1. - long count() 返回此流中的元素数。

ArrayList<String> list = new ArrayList<>();
list.add(“张三”);
list.add(“张大四”);
list.add(“张小六”);
list.add(“李三”);
list.add(“大四三”);
list.add(“小六”);
list.add(“三三”);
list.add(“张张”);
// 使用Stream流的forEach()终结流并输出
list.stream().forEach(System.out::println);
// 统计结合中开头为张的元素有多少,使用count()方法
long 张 = list.stream().filter(s -> s.startsWith(“张”)).count();
System.out.println(); //4

Stream流的练习

案例1:
现在又两个ArrayList集合,分别存储6名男演员和6名女演员的名称,要求完成如下操作

  • 男演员只要名字为3个字的前三人
  • 女演员只要姓林的,并且不要第一个
  • 把过滤后的男演员姓名和女演员姓名合并到一起
  • 把上一步操作后端元素作为构造方法的参数创建演员对象,遍历数据
    • 演员Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量的get/set方法

ArrayList<String> male = new ArrayList<>();
male.add(“王五”);
male.add(“李大四”);
male.add(“张小六”);
male.add(“习大大”);
male.add(“王三”);
male.add(“燕小六”);
male.add(“三哥”);
male.add(“小小张”);
—————————————————-
ArrayList<String> female = new ArrayList<>();
female.add(“林青霞”);
female.add(“林天海”);
female.add(“王心雨”);
female.add(“郭林”);
female.add(“林琳”);
female.add(“张曼玉”);
——————————————————-
// 男演员只要名为3个字的前三个人
Stream<String> 男 = male.stream().filter(s -> s.length() == 3).limit(3);
// 女演员只要性林的不要第一个
Stream<String> 女 = female.stream().filter(s -> s.startsWith(“林”)).skip(1);
// 把过滤后的男演员和女演员合并到一起
Stream<String> 演员 = Stream.concat(男, 女);
// 将上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
// 演员.map(String::format).forEach(System.out::println);
// 将数据座位元素到构造方法中,重写toString()
// 演员.map(Actor::new).forEach(System.out::println);
// 使用forEach获取类的get方法
演员.map( (String s)-> {
return new Actor(s);
}).forEach(System.out::println);
演员.map(Actor::new).forEach(s->{ System.out.println(s.getName()); });
——————————————————————
// 创建一个类,接受数据
public class Actor {
private String name;
public Actor(String name) { this.name = name; }
public Actor() { }
public String getName() { return name; }
public void setName(String name) { this.name = name; } }

Stream流的收集操作

对数据使用Stream流的方式操作完毕后,我想把Stream流中的数据收集到集合中,该怎么做?

Stream流的收集方法

  • R collect(Collector collector)
  • 该收集方法的参数是一个Collector接口

    R

    collect(Collector<? super T,A,R> collector)
    使用 Collector对此流的元素执行 mutable reductionCollector 。

    Class Collectors

    的实施方式中Collector实现各种有用的还原操作,如累加元件到集合,根据各种标准总结元件等

    • public static Collector toList(): 把元素收集到List集合中
    • public static Collector toSet(): 把元素收集到Set集合中
    • public static Collector toMap(Function keyMapper,Function valueMapper): 把元素收集到Map集合中

// Stream流数据收集到List集合中使用collect(Collectors.toList)
ArrayList<String> list = new ArrayList<>();
list.add(“王五”);
list.add(“李大四”);
list.add(“张小六”);
list.add(“习大大”);
list.add(“王三”);
list.add(“燕小六”);
list.add(“三哥”);
list.add(“小小张”);
// 得到名字为3个字的流
Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
// 把流中的数据集合到List集合中并遍历
List<String> name = listStream.collect(Collectors.toList());
for (String s: name){ System.out.println(s);}
————————————————————————————
// Stream流数据收集到set集合中使用collect(Collectors.toSet)
Set<Integer> set = new HashSet<>();
set.add(10);
set.add(32);
set.add(23);
set.add(28);
set.add(55);
set.add(12);
// 得到年龄大于25的流数据
Stream<Integer> setStream = set.stream().filter(i -> i > 25);
// 把流中的数据加载到set集合中
Set<Integer> age = setStream.collect(Collectors.toSet());
// Stream流数据收集到set集合中使用collect(Collectors.toMap(key,value))
// 定义一个字符数组,每一个字符串数据由姓名和年龄组合
String[] strArray = {“张三,20”, “赵六,10”, “王五,39”, “李四,38”};
// 得到字符串中年龄数据大于28的流
// Stream name = Arrays.stream(strArray).filter(s -> Integer.parseInt(s.split(“,”)[1]) > 28);
// 使用Stream流中的of(T…values)方法获取数组
Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(“,”)[1]) > 28);
// 把Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名为key,年龄为value
// 使用collect(Collectors.toMap())获取Stream的数据
Map<String, Integer> mapCollect = arrayStream.collect(Collectors.toMap(s -> s.split(“,”)[0], s -> Integer.parseInt(s.split(“,”)[1])));
Set<String> keys = mapCollect.keySet();
for (String key : keys) {
int value = mapCollect.get(key);
System.out.println(key + “:” + value);**}