Stream流实例
需求:
1. 创建一个集合,存储多个字符串元素
1. 把集合中所有以"张"开头的元素存储到一个新的集合中
1. 把"张"开头的集合中的长度为3的元素存储到一个新的集合中
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流的方式完成过滤操作
- xx.stream().filter(Predicate判断函数式接口).forEach(Consumer消费函数式接口);
- Stream可以很好的提升代码的阅读性,可以完美展示无关逻辑方式的语义: 生成流、过滤、打印
- Stream是真正的将函数式编程风格引入到Java中
- <br />
Stream流的生成方式
Stream流的使用过程
生成流
- 通过数据源(集合,数组等)生成流
-
中间操作流
一个Stream流一旦生成后,可以跟随多个中间操作,其目的就是讲生成的流打开,然后对打开流的数据进行操作(过滤/映射),然后返回一个新的流,交给下一个操作使用
-
终结操作流
一个流只能有一个终结操作,当这个操作执行后,流就被关闭了,无法再操作. 所以Stream流的最后一个操作一定是forEach()
-
Stream流的生成方式
Stream流的常见生成方式
Collection体系的集合可以使用默认方法stream()生成流
- default Stream
stream()
- default 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
返回由与此给定谓词匹配的此流的元素组成的流。用于对流中的数据进行过滤
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
— 返回由该流的元素组成的流,截断长度不能超过maxSize 。
- 返回流中元素组成的流,截取前指定参数个数的数据
skip
—- Stream
— 在丢弃流的第一个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
返回由该流的不同元素(根据Object.equals(Object) )组成的流。
concat
static
合并Stream a流和Stream b流为一个流。
创建一个懒惰连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。如果两个输入流都被排序,则生成的流被排序,并且如果任何一个输入流是并行的,则并行。当结果流关闭时,调用两个输入流的关闭处理程序。
// 取前4个元素组成一个流,创建对象获取这个流
Stream<String> limit = list.stream().limit(4);
// 跳过2个元素组成一个流,创建对象获取这个流
Stream<String> skip = list.stream().skip(2);
// Stream
// 合并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(Comparator)
Stream
返回由该流的元素组成的流,根据提供的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
返回由给定函数应用于此流的元素的结果组成的流。
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
- void forEach(Consumer<? super T> action) 对此流的每个元素执行操作。
count
- 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)
-
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集合中
- public static
// 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
// 使用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);**}