JAVA中关于Stream流的使用
1 集合处理的弊端
当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:
public class Demo01 {public static void main(String[] args) {// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");// 1.拿到所有姓张的ArrayList<String> zhanglist = new ArrayList<String>();for (String string : list) {if (string.startsWith("张")) {zhanglist.add(string);}}System.out.println(zhanglist);ArrayList<String> threeList = new ArrayList<String>();// 2.拿到名字长度为3的字for (String string : zhanglist) {if (string.length() == 3) {threeList.add(string);}}System.out.println(threeList);// 这段代码中含有三个循环,每一个作用不同:
// 1. 首先筛选所有姓张的人;
// 2. 然后筛选名字有三个字的人;
// 3. 最后进行对结果进行打印输出。
// 每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。// 这是理所当然的么? 不是。循环 是做事情的方式,而不是目的。// 每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,// 只能再使 用另一个循环从头开始。
// 那Stream能给我们带来怎样更加优雅的写法呢?System.out.println("==========================");// 使用Stream流的方式来完成此处的任务list.stream().filter(string -> string.startsWith("张")).filter(string -> string.length() == 3).forEach(System.out::println);}}
// 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:
// 获取流、过滤姓张、过滤长度为3、逐一打印。
// 我们真 正要做的事情内容被更好地体现在代码中。
Stream流思想概述
注意: Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象!
Stream流式思想类似于工厂车间的“生产流水线” ,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。 Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
2 获取Stream流的方式
java.util.stream.Stream<T>是JDK 8新加入的流接口。 获取一个流非常简单,有以下几种常用的方式:
Collection集合提供了 default Stream<E> stream()获取流;
Stream接口的静态方法 of 可以获取对应的流。
public class Demo02 {public static void main(String[] args) {// 方式1:List<String> list = new ArrayList<String>(Arrays.asList("aa","bb","cc"));Stream<String> stream1 = list.stream();System.out.println("============================");stream1.forEach(System.out::println);Set<String> set = new HashSet<String>(Arrays.asList("aa","bb","cc"));Stream<String> stream2 = set.stream();System.out.println("============================");stream2.forEach(System.out::println);Map<Integer, String> map = new HashMap<Integer, String>(){{put(1, "aa");put(2, "bb");put(3, "cc");}};Stream<Integer> stream3 = map.keySet().stream();Stream<String> stream4 = map.values().stream();Stream<Map.Entry<Integer, String>> stream5 = map.entrySet().stream();System.out.println("============================");stream3.forEach(System.out::println);System.out.println("============================");stream4.forEach(System.out::println);System.out.println("============================");stream5.forEach(System.out::println);// 方式2:Stream<String> stream6 = Stream.of("aa", "bb", "cc");System.out.println("============================");stream6.forEach(System.out::println);String[] strs = {"aa", "bb", "cc"};Stream<String> stream7 = Stream.of(strs);System.out.println("============================");stream7.forEach(System.out::println);int[] ints1= {1,2,3,4,5};int[] ints2= {0,6,7,8,9};Stream<int[]> stream8 = Stream.of(ints1,ints2);System.out.println("============================");stream8.forEach(System.out::println);}
}
3 Stream流的常用方法和注意事项
Stream流常用方法
Stream流模型的操作很丰富,这里介绍一些常用的API。
这些方法可以被分成两种:
终结方法:返回值类型不再是 Stream类型的方法,不再支持链式调用。本小节中,终结方法包括 count和 forEach 方法。
非终结方法:返回值类型仍然是 Stream类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结 方法。)
public class Demo03 {public static void main(String[] args) {Stream<String> stream = Stream.of("aa", "bb", "cc","dd");
// long count = stream.count();
// System.out.println(count);// long count1 = stream.count(); //报错//2.只要对流做操作,返回的就不是原来的流,而是一个新流Stream<String> stream2 = stream.limit(3);
// stream2.forEach(System.out::println);//3.不使用终结方法,中间的操作不会执行stream2.filter(str->{System.out.println(str);return true;}).count();}
}
1 forEach方法
forEach 用来遍历流中的数据 void forEach(Consumer<? super T> action);
该方法接收一个 Consumer接口函数,会将每一个流元素交给该函数进行处理。例如:
List<String> list = new ArrayList<String>();
Collections.addAll(list,"迪丽热巴","古力娜扎","刘亦菲","赵雅芝");
//1.获取流 遍历集合中的元素
Stream<String> stream=list.stream();
stream.forEach(str-> System.out.println(str));
2 count方法
Stream流提供 count方法来统计其中的元素个数:long count();
该方法返回一个long值代表元素个数。基本使用:
List<String> list = new ArrayList<String>();
Collections.addAll(list,"迪丽热巴","古力娜扎","刘亦菲","赵雅芝");//2.Stream流提供count方法来统计其中的元素个数
long count=stream.count();
System.out.println(count);
3 filter方法
filter用于过滤数据,返回符合过滤条件的数据,可以通过 filter方法将一个流转换成另一个子集流。
该方法接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。
List<String> list = new ArrayList<String>();
Collections.addAll(list,"迪丽热巴","古力娜扎","刘亦菲","赵雅芝");//3.filter用于过滤器,返回符合过滤条件的数据
Stream<String> stream1=stream.filter(str->str.length()==3);
stream1.forEach(System.out::println);
4 limit方法
limit方法可以对流进行截取,只取用前n个。参数是一个long型,如果集合当前长度大于参数则进行截取。否则不进行操作。
List<String> list = new ArrayList<String>();
Collections.addAll(list,"迪丽热巴","古力娜扎","刘亦菲","赵雅芝");//4.limit方法可以对流进行截取,只取用前n个
stream.limit(3).forEach(System.out::println);
5 skip方法
如果希望跳过前几个元素,可以使用 skip方法获取一个截取之后的新流。如果流的当前长度大于n ,则跳过前n个;否则将会得到一个长度为0的空流。
List<String> list = new ArrayList<String>();
Collections.addAll(list,"迪丽热巴","古力娜扎","刘亦菲","赵雅芝");//5.如果希望跳过前几个元素,可以使用 skip方法获取一个截取之后的新流。
stream.skip(2).forEach(System.out::println);
6 map方法
Map可以将一种类型的流转换成另一种类型的流。该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
ArrayList<String> list = new ArrayList<>(Arrays.asList("32","22","15","80","58"));
Stream<String> stream = list.stream();
//6.Map可以将一种类型的流转换成另一种类型的流
Stream<Integer> stream1=stream.map(string -> Integer.parseInt(string));
stream1.forEach(System.out::println);
7 sorted方法
如果需要将数据排序,可以使用 sorted方法。
ArrayList<String> list = new ArrayList<>(Arrays.asList("32","22","15","80","58"));
Stream<String> stream = list.stream();
//6.Map可以将一种类型的流转换成另一种类型的流
Stream<Integer> stream1=stream.map(string -> Integer.parseInt(string));
// stream1.forEach(System.out::println);
System.out.println("===============sorted===================");//如果需要将数据排序,可以使用sorted()方法
//使用泛型类型的默认排序规则方法
// stream1.sorted().forEach(System.out::println);//使用自定义的Comparator比较规则排序
stream1.sorted((a,b)->b-a).forEach(System.out::println);
8 distinct方法
如果需要去除重复数据,可以使用 distinct方法。
ArrayList<String> list = new ArrayList<>(Arrays.asList("32","22","15","80","58"));
Stream<String> stream = list.stream();
//6.Map可以将一种类型的流转换成另一种类型的流
Stream<Integer> stream1=stream.map(string -> Integer.parseInt(string));System.out.println("================distinct===============");
//去重
Stream.of(1,3,5,3,7,3,8,7).distinct().forEach(System.out::println);
9 match方法
如果需要判断数据是否匹配指定的条件,可以使用 Match相关方法。
System.out.println("===============match==================");
//如果需要判断数据是否匹配指定的条件,可以使用Match相关方法
//所有的元素匹配条件,则返回true
boolean b = Stream.of(1,4,6,7,5,8,9,6,4).allMatch(number -> number % 2 == 0);
System.out.println("是否都为偶数?"+b);//只要有一个匹配条件,则返回true
boolean b1 = Stream.of(1,4,6,7,5,8,9,6,4).anyMatch(number -> number % 2 == 0);
System.out.println("是否有偶数?"+b1);//所有的元素都不匹配,则返回true
boolean b3 = Stream.of(1,4,6,7,5,8,9,6,4).noneMatch(number -> number % 2 == 0);
System.out.println("是否都不为偶数?"+b1);
10 collect方法
collect() 收集是一个 最终操作,返回Stream
中元素集合,返回值类型是集合(List、Set、Map)或 字符串。
将Stream中的元素,收集至新集合
Collectors.toList()
Collectors.toSet()
Collectors.toMap()
public class Demo06 {public static void main(String[] args) {List<String> list = Arrays.asList("abc", "defg", "ghi","ghi","ijkl", "ijkl","ijkl","mno");//将过滤结果,收集至List集合List<String> list1 = list.stream().filter(s->s.toUpperCase().contains("I")).collect(Collectors.toList());System.out.println(list1);System.out.println("=============================");//将过滤结果,收集到set集合Set<String> set = list.stream().filter(s->s.toUpperCase().contains("I")).collect(Collectors.toSet());System.out.println(set);System.out.println("=============================");//将过滤结果,收集至Map集合Map<String,Integer> map=list.stream().distinct().collect(Collectors.toMap(s->s,s->s.length()));System.out.println(map);System.out.println("=============================");//按照字符串长度统计Map<Integer,List<String>> map1=list.stream().collect(Collectors.groupingBy(s->s.length()));System.out.println(map1);System.out.println("=============================");//按照字符串长度转成集合List<Integer> mappingList=list.stream().collect(Collectors.mapping(s->s.length(),Collectors.toList()));System.out.println(mappingList);}
}
11 Parallel Streams 并行流
Stream
有串行和并行两种,串行Stream
上的操作是在一个线程中依次完成,而并行Stream
则是在多个线程上同时执行。
public class Demo07 {public static void main(String[] args) {int max = 1000000;List<String> values = new ArrayList<>(max);for (int i = 0; i < max; i++) {UUID uuid = UUID.randomUUID();values.add(uuid.toString());}long t0 = System.nanoTime();//返回豪微秒
// values.stream().sorted().forEachOrdered(t -> System.out.println(t));values.parallelStream().sorted().forEachOrdered(t -> System.out.println(t));long t1 = System.nanoTime();long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);System.out.println(String.format("串行排序,耗时共计: %d 毫秒", millis));}
}
4 Stream流注意事项
1. Stream只能操作一次
2. Stream方法返回的是新的流
3. Stream不调用终结方法,中间的操作不会执行