引例:
1 ListstrList = Arrays.asList("zhaojigang","nana","tianya","nana");2 Stream streamList = strList.stream();//集合转为stream3 strList = streamList.distinct().filter(str->!str.equals("tianya")).sorted(String::compareTo).collect(Collectors.toList());4 strList.forEach(System.out::println);
说明:
- 第一行:创建数组并转为List
- 第二行:根据List创建stream
- 第三行:对该stream进行去重-->选择-->排序-->stream转为List
- 第四行:遍历该List
以上代码显示了stream API的方便。当然,上边的代码可以更为简洁,如下改为一行:
Arrays.asList("zhaojigang","nana","tianya","nana").stream().distinct().filter(str->!str.equals("tianya")).sorted(String::compareTo).collect(Collectors.toList()).forEach(System.out::println);
以上代码有一个易错点:filter是选择而不是过滤,即filter是选择满足条件的元素。
一、创建Stream
三种常用API:
- 集合-->Stream:stream()
- 数组-->Stream:Stream.of(T t)或者Arrays.stream(T[] t)
- 任意元素-->Stream:Stream.of(T... values)
1 ListstrList = Arrays.asList("zhaojigang","nana","tianya","nana"); 2 Stream streamList = strList.stream();//集合转为stream 3 4 String[] strArray = {"java","c++","c"}; 5 Stream streamArray = Stream.of(strArray);//数组转为Stream 6 Stream streamArray2 = Arrays.stream(strArray);//数组转为Stream 7 8 Stream streamPartArray = Arrays.stream(strArray, 0, 2);//转换部分数组,范围:[0,2) 9 10 Stream streamSelf = Stream.of("python","basic","php");//任意元素
还有一种:用于产生无限流的,Stream.generate(Supplier<T> s)。
二、Stream 2 array/collection/String/map
1、stream2array
1 StreamstrStream = Stream.of("java","c++","c","python");2 Object[] objectArray = strStream.toArray();//只能返回Object[]3 String[] strArray = strStream.toArray(String[]::new);//构造器引用(类似于方法引用),可以返回String[]
说明:
通过构造器引用(类似于方法引用),可以构造出具体类型的数组。
2、stream2collection
1 ListstrList = strStream.collect(Collectors.toList());//返回List2 Set strSet = strStream.collect(Collectors.toSet());//返回set3 ArrayList strArrayList = strStream.collect(Collectors.toCollection(ArrayList::new));//收集到指定的List集合,例如收集到ArrayList
说明:
通过构造器引用,可以构造出具体类型的集合。
3、将stream中的元素拼接起来(joining()、joining(","))
1 StreamstrStream = Stream.of("java","c++","c","python");2 String str = strStream.collect(Collectors.joining());//将所有字符串拼接起来,结果:javac++cpython3 System.out.println(str);4 5 String str2 = strStream.collect(Collectors.joining(","));//将所有字符串拼接起来,中间用","隔开,结果:java,c++,c,python6 System.out.println(str2);
arryIds.stream().map(Object::toString).collect(Collectors.joining(","));
4、stream2map(toMap、toConcurrentMap)
1 StreamstrStream = Stream.of("java","c++","c","python"); 2 Map map1 = strStream.collect(Collectors.toMap(Function.identity(), (x)->0)); 3 //Function.identity()-->返回strStream中的元素,toMap方法的我两个参数都是Function接口型的,所以第二个参数即使只放0,也不能直接写作0,可以使用如上的方式进行操作 4 5 for(String key : map1.keySet()){ 6 System.out.println("key:"+key+"->"+"value:"+map1.get(key)); 7 } 8 //结果 9 /*10 key:python->value:011 key:c++->value:012 key:c->value:013 key:java->value:014 */
说明:
- toMap-->stream转为map
- Function.identity()-->返回stream中的元素
如果key重复的话,这时就会出现问题"duplicate key",采用如下方式解决(增加第三个参数):
1 StreamstrStream = Stream.of("java","c++","c","python","java");2 Map map1 = strStream.collect(Collectors.toMap(Function.identity(), //key3 (x)->0, //value4 (existingValue, newValue) -> existingValue));//如果key重复,取旧值
需要指定返回map的具体类型(增加第四个参数)。
1 Mapmap1 = strStream.collect(Collectors.toMap(Function.identity(), //key2 (x)->0, //value3 (existingValue, newValue) -> existingValue,//如果key重复,取旧值4 TreeMap::new));//返回TreeMap
注意:每一个toMap就会对应一个相应的toConcurrentMap
5、groupingBy partitioningBy
1 /***************************groupingBy partitioningBy**************************/ 2 StreamlocaleStream = Stream.of(Locale.getAvailableLocales()); 3 Map > country2localeList = localeStream.collect(Collectors.groupingBy(Locale::getCountry));//根据国家分组,groupBy的参数是分类器 4 List locales = country2localeList.get("CH"); 5 6 Map > country2localeSet = localeStream.collect(Collectors.groupingBy(Locale::getCountry, Collectors.toSet()));//根据国家分组,groupBy的参数是分类器,返回set 7 Set localeSet = country2localeSet.get("CH"); 8 9 Map > country2locales = localeStream.collect(Collectors.partitioningBy(locale->locale.getLanguage().equals("en")));//分成两组,一组为true(即语言是en的),一组为false(即语言不是en的)10 List trueLocale = country2locales.get(true);
三、filter(Predicate p)
注意:是选择而非过滤。
1 StreamstreamSelf = Stream.of("python","basic","php");2 streamSelf.filter(str->str.startsWith("p")).forEach(System.out::println);
注意:
- stream也是可以foreach的,没必要一定要转化成集合再foreach
更好的写法可能是下边这种:
1 PredicatestartCondition = str->str.startsWith("p");2 streamSelf.filter(startCondition).forEach(System.out::println);
说明:将条件(通常是lambda表达式)抽取出来。这种方式在多个条件的情况下比较清晰。
注意:函数式接口 = lambda表达式 (即lambda表达式只能返回为函数式接口)
1 Streams = Stream.of("java1","java3","java","php12");2 Predicate condition1 = str->str.length()==5;//条件13 Predicate condition2 = str->str.startsWith("j");//条件24 s.filter(condition1.and(condition2)).forEach(System.out::println);//and条件
说明:
多条件运算:and or
四、map(Function mapper)
作用:对流中的每一个元素进行操作。
1 StreamstreamSelf = Stream.of("python","basic","php");2 streamSelf.map(String::toUpperCase).forEach(System.out::println);
说明:将流内的每一个String全部转换为了大写。
五、reduce
作用:对stream中的每一个元素做聚合操作。
1 StreamreduceStream = Stream.of(1,2,3,4,5);2 Optional sumOption = reduceStream.reduce((x,y)->x+y);//计算1+2+3+4+5,即对元素中的元素进行聚合计算,而map是对元素中的每一个元素分别计算(注意:如果stream为null的话,就会产生无效的结果,需要使用Optional接收)3 //Optional sumOption = reduceStream.reduce(Integer::sum);//计算1+2+3+4+5,即对元素中的元素进行聚合计算,而map是对元素中的每一个元素分别计算4 5 Integer result = reduceStream.reduce(0, Integer::sum);//0为标识值,即计算:0+1+2+。。+5,如果整个stream为null,就返回标识值。6 System.out.println(result);
注意:以上是reduce的简单形式,即内联函数是(T,T)->T,即返回值和参数类型是一样的,返回值和参数类型不同的场景需要自己编写函数(用的较少)
六、Optional
两种用法:
- ifPresent(xxx):存在的就执行xxx,不存在就什么都不执行
- orElse(xxx):存在就返回存在的值,不存在就返回xxx(可以理解为是默认值)
1 StreamoptionalStream = Stream.of("java","python","basic");2 Optional optionValue = optionalStream.filter(str->str.startsWith("p")).findFirst();3 optionValue.ifPresent(str->System.out.println(str));//if optionalValue为true,即str存在,则输出str,当然也可以使用如下4 String str = optionValue.orElse("xxx");//如果optionValue为false,即不存在以p开头的字符串时,使用"xxx"来替代5 System.out.println(str);
Optional是Java8提供的为了解决null安全问题的一个API。善用Optional可以使我们代码中很多繁琐、丑陋的设计变得十分优雅。这篇文章是建立在你对Optional的用法有一定了解的基础上的,如果你还不太了解Optional,可以先去看看相关教程,或者查阅Java文档。
使用Optional,我们就可以把下面这样的代码进行改写。
public static String getName(User u) { if (u == null) return "Unknown"; return u.name;}
不过,千万不要改写成这副样子。
public static String getName(User u) { Optionaluser = Optional.ofNullable(u); if (!user.isPresent()) return "Unknown"; return user.get().name;}
这样改写非但不简洁,而且其操作还是和第一段代码一样。无非就是用isPresent方法来替代u==null。这样的改写并不是Optional正确的用法,我们再来改写一次。
public static String getName(User u) { return Optional.ofNullable(u) .map(user->user.name) .orElse("Unknown");}
这样才是正确使用Optional的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。看一段代码:
public static String getChampionName(Competition comp) throws IllegalArgumentException { if (comp != null) { CompResult result = comp.getResult(); if (result != null) { User champion = result.getChampion(); if (champion != null) { return champion.getName(); } } } throw new IllegalArgumentException("The value of param comp isn't available.");}
由于种种原因(比如:比赛还没有产生冠军、方法的非正常调用、某个方法的实现里埋藏的大礼包等等),我们并不能开心的一路comp.getResult().getChampion().getName()到底。而其他语言比如kotlin,就提供了在语法层面的操作符加持:comp?.getResult()?.getChampion()?.getName()。所以讲道理在Java里我们怎么办!
让我们看看经过Optional加持过后,这些代码会变成什么样子。
public static String getChampionName(Competition comp) throws IllegalArgumentException { return Optional.ofNullable(comp) .map(c->c.getResult()) .map(r->r.getChampion()) .map(u->u.getName()) .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));}
这就很舒服了。Optional给了我们一个真正优雅的Java风格的方法来解决null安全问题。虽然没有直接提供一个操作符写起来短,但是代码看起来依然很爽很舒服。更何况?.这样的语法好不好看还见仁见智呢。
还有很多不错的使用姿势,比如为空则不打印可以这么写:
string.ifPresent(System.out::println);
Optional的魅力还不止于此,Optional还有一些神奇的用法,比如Optional可以用来检验参数的合法性。
public void setName(String name) throws IllegalArgumentException { this.name = Optional.ofNullable(name).filter(User::isNameValid) .orElseThrow(()->new IllegalArgumentException("Invalid username."));}
这样写参数合法性检测,应该足够优雅了吧。
参考资料
-
使用 Java8 Optional 的正确姿势 – 隔叶黄莺 Unmi Blog (https://unmi.cc/proper-ways-of-using-java8-optional/)
https://mp.weixin.qq.com/s/p_vwjlpK6GxQBk5_nSbeQA
七、limit skip contact
1、limit(long size)
作用:截取stream的前size个元素。
1 StreamstreamSelf = Stream.of("python","basic","php");2 streamSelf.limit(2).forEach(System.out::println);//截取前两个
2、skip(long size)
作用:跳过stream的钱size个元素
1 StreamstreamSelf = Stream.of("python","basic","php");2 streamSelf.skip(2).forEach(System.out::println);//跳过前两个
3、contact(Stream<T>,Stream<T>)
作用:拼接两个stream
1 StreamstreamSelf = Stream.of("python","basic","php");2 Stream streamSelf2 = Stream.of("python2","basic2","php2");3 Stream.concat(streamSelf, streamSelf2).forEach(System.out::println);
八、聚合函数 count max min findFirst findAny anyMatch allMatch noneMatch
1 StreamstreamSelf = Stream.of("python","basic","php","b");2 System.out.println(streamSelf.count());//计算流中的元素个数3 Optional largest = streamSelf.max(String::compareToIgnoreCase);//寻找最大值4 if(largest.isPresent()){5 System.out.println(largest.get());6 }
说明:min函数也一样。
注意:Optional的使用,上边的是最差的一种形式,见"六"。
1 OptionalfirstMatch = streamSelf.filter(str->str.startsWith("b")).findFirst();//寻找第一个符合条件的元素 2 firstMatch.ifPresent(System.out::println);//这是Optional的第一种用法 3 4 Optional anyMatch = streamSelf.parallel().filter(str->str.startsWith("b")).findAny();//返回集合中符合条件的任意一个元素,对于并行处理非常好(因为多个线程只要有一个线程找到了,整个计算就会结束) 5 if(anyMatch.isPresent()){ 6 System.out.println(anyMatch.get());//这里的结果可能是b,有可能是basic 7 } 8 9 boolean isAnyMatch = streamSelf.parallel().anyMatch(str->str.startsWith("c"));//集合中是否有一个满足条件10 System.out.println(isAnyMatch);11 12 Stream streamSelf3 = Stream.of("basic","b");13 boolean isAllMatch = streamSelf3.parallel().allMatch(str->str.startsWith("b"));//集合中是否所有元素都满足条件14 System.out.println(isAllMatch);15 16 boolean isAllNotMatch = streamSelf.parallel().noneMatch(str->str.startsWith("p"));//集合中是否没有一个元素满足条件17 System.out.println(isAllNotMatch);
注意:
- optional的最佳用法:ifPresent()-->如果有就输出,如果没有,什么都不做
- parallel():将stream转为并行流,并行流的使用一定要注意线程安全
九、原始类型流
- IntStream:int、short、char、byte、boolean
- LongStream:long
- DoubleStream:double、float
http://www.cnblogs.com/java-zhao/p/5492122.html