java8新特性(二)Stream流
-
由需求初识Stream
-
有一份菜单,需求在其中找出卡路里低于指定值的菜单,且按照指定顺序对菜单排序,要求仅输出满足条件的菜单的名字
//菜单类 @Data @Builder @AllArgsConstructor public class Dish{ private final String name; private final boolean vegetarian; private final int calories; private final Type type; public enum Type {MEAT,OTHER,FISH} } // public class SimpleStream { public static void main(String[] args) { //菜中选择某一指定calories菜,并按照指定顺序排列输出 List<Dish> menu = Arrays.asList( Dish.builder().name("pork").vegetarian(false).calories(800).type(Dish.Type.MEAT).build(), Dish.builder().name("beef").vegetarian(false).calories(700).type(Dish.Type.MEAT).build(), Dish.builder().name("chicken").vegetarian(false).calories(400).type(Dish.Type.MEAT).build(), Dish.builder().name("french").vegetarian(true).calories(530).type(Dish.Type.OTHER).build(), Dish.builder().name("rice").vegetarian(true).calories(350).type(Dish.Type.OTHER).build(), Dish.builder().name("season fruit").vegetarian(true).calories(120).type(Dish.Type.OTHER).build(), Dish.builder().name("pizza").vegetarian(true).calories(550).type(Dish.Type.OTHER).build(), Dish.builder().name("prawns").vegetarian(false).calories(300).type(Dish.Type.FISH).build(), Dish.builder().name("salmon").vegetarian(false).calories(450).type(Dish.Type.FISH).build() ); //过滤卡路里小于400,排序输出 //1.1 List<String> dishNamesByCollections = getDishNamesByCollections(menu); System.out.println(dishNamesByCollections); //1.2 List<String> dishNamesByStream = getDishNamesByStream(menu); System.out.println(dishNamesByStream); //2.流具有中断特性,以下操作会报错stream has already been operated upon or closed /*Stream<Dish> stream = menu.stream(); stream.forEach(System.out::println); stream.forEach(System.out::println);*/ //3.Stream流中的filter和map //filter用来过滤满足条件的数据数据类型不会发生改变,只是用来筛选满足条件的数据 //map用来匹配,可以输出不同的类型。如只想要Dish中的名字,当然,也可以毫不改变的输出数据 List<String> result = menu.stream().filter(d -> { System.out.println("filtering->" + d.getName()); return d.getCalories() > 300; }).map(d -> { System.out.println("map->" + d.getName()); return d.getName(); }).limit(3).collect(toList()); //4.Stream流中的遍历 Stream<Dish> stream = Stream.of( Dish.builder().name("pork").vegetarian(false).calories(800).type(Dish.Type.MEAT).build(), Dish.builder().name("beef").vegetarian(false).calories(700).type(Dish.Type.MEAT).build(), Dish.builder().name("chicken").vegetarian(false).calories(400).type(Dish.Type.MEAT).build(), Dish.builder().name("french").vegetarian(true).calories(530).type(Dish.Type.MEAT).build(), Dish.builder().name("rice").vegetarian(true).calories(350).type(Dish.Type.MEAT).build()); stream.forEach(System.out::println); } private static List<String> getDishNamesByCollections(List<Dish> menu){ List<String> temp = new ArrayList<>(); for(Dish dish:menu){ if(dish.getCalories()<400){ temp.add(dish); } } //之前的排序方式 temp.sort(new Comparator<Dish>() { @Override public int compare(Dish o1, Dish o2) { return o1.getCalories()>o2.getCalories(); } }); //使用lambda后 Collections.sort(temp, (d1, d2) -> Integer.compare(d1.getCalories(), d2.getCalories())); //Collections.sort(temp,Comparator.comparing(Dish::getCalories)); List<String> dishNameList = new ArrayList<>(); for (Dish d : temp) { dishNameList.add(d.getName()); } return dishNameList; } private static List<String> getDishNamesByStream(List<Dish> menu) { //Predicate: boolean test(T t); return menu.stream().filter(d -> d.getCalories() < 400).sorted(Comparator.comparing(Dish::getCalories)) .map(Dish::getName).collect(toList()); } }
-
-
Stream流简介
- stream是一个升级版的java API,可用于操作集合、IO以及自定义的操作,且可以真正实现并行化处理。(Stream中的spliterator()实现,底层基于fork-join机制实现)。
- 相比集合操作而言,stream将对集合的操作封装在内部,而collection则是用户取出其中的元素,进行相应的操作。
-
Stream流相关知识点
-
Stream流中涉及的几个概念
- Sequence of elements:类似于collection中元素,即上面例子中Dish中的元素。
- Source:类似于collections、I/O resources等,即上面例子中的dishList集合。
- Data procession operations:类似于filter/map/reduce/find/match/sort等,即上面例子中对集合的map等操作。
- Pipelining:方法操作返回流,可以进行链式调用。
- Internal iteration:所有的一系列操作都发生在内部。
-
Stream流的操作类型:
a. 中断的流操作(Trabersable only once):Internal iteration
Stream<Dish> res = menuList.stream() res.forEach(System.out::println); //正确输出 res.forEach(System.out::println); //报错,上一个流已被操作或者关闭
b. 不可中断的操作(External):External iteration
-
Stream流常用的操作分类
-
Intermediate operations:操作会产生一个新的stream,相同类型,可以进行链式操作。此类操作有:filter/map/limit/sorted/distinct等
menu.stream().filter(d->{ System.out.printIn("filter"+d.getName()); return d.getCalories()>300; }) .map(d->{ System.out.printIn("map"+d.getName()); return d.getName(); }) .limit(3).collect(toList()); 输出结果: filter->xxx map->xxx filter->yyy map->yyy ...
-
Terminal operations:操作会中断,如上面例子中的res.forEach操作,此类操作有:reduce,forEach,count,collect等等。
-
-
Stream流的创建
-
通过Collection接口,其中提供了Stream stream()创建stream,可以通过Collection创建
private static Stream<String> getStreamFromCollection(){ List<String> list = Arrays.asList("hello","java"); return list.stream(); }
-
通过可变长度values创建
private static Stream<String> getStreamFromValues(){ return Stream.of("hello","java"); }
-
通过arrays创建
private static Stream<String> getStreamFromArrays(){ return Arrays.stream(new String[]{"hello","stream"});7 }
-
通过文件创建
private static Stream<String> getStreamFromFile(){ Path path = Paths.get("具体路径"); try(Stream<String> lines = Files.lines(path)){ lines.forEach(System.out::println); return lines; }catch(IOException e){ throw new RuntimeException(e); } }
-
通过iterator迭代创建
private static Stream<Integer> getStreamFromIterator(){ //0,2,4,6,8,10 Stream<Integer> stream = Stream.iterate(0,n->n+2).limit(6); return stream; }
-
通过Generate创建,generate传入一个Supplier即可
private static Stream<Double> createStreamFromGenerate(){ return Stream.generate(Math::random).limit(5); }
-
自定义对象获取stream流
static class Obj{ private int id; private String name; //ignore getter and setter } static class ObjSupplier implements Supplier<Obj>{ private int index = 0; private Random random = new Random(System.currentTimeMillis()); @Override public Obj get(){ index = random.nextInt(100); return new Obj(index,"name is "+index); } } private static Stream<Obj> createObjStreamFromGenerate(){ return Stream.generate(new ObjSupplier()).limit(10); }
-
-
-
Stream流中涉及的方法
-
filter,map,limit,skip,distinct,flatmap方法
//1.filter:传入一个Predicate,用来过滤 List<Integer> elements = Arrays.asList(1,2,3,3,4,5,6,7,8); //过滤偶数元素 List<Integer> res = elements.stream().filter(a->a%2==0).collect(toList()); //元素的去重 res = elements.stream().distinct().collect(toList()); //元素的截断 res = elements.stream().skip(5).collect(toList()); //[5,6,7,8] res = elements.stream().limit(5).collect(toList());//[1,2,3,3,4] //2.map,传入一个Function,用来匹配 List<Integer> elements = Arrays.asList(1, 2, 2, 3, 3, 4, 5, 5, 5, 6); //对元素进行加工,乘以2 List<Integer> res = elements.stream().map(a -> a*2).collect(toList()); //条件过滤 @Data @Builder class Person{ private String name; private Integer age; } List<Person> persons = Arrays.asList( Person.builder().name("zs").age(21).build(), Person.builder().name("ls").age(22).build(), Person.builder().name("ww").age(23).build(), Person.builder().name("zl").age(24).build(), Person.builder().name("wb").age(25).build() ); List<String> pers = persons.stream().map(a->a.getName()).collect(toList()); List<String> pers = persons.stream().map(a->a.getName()).collection(toList()); persons.stream().map(a->a.getName).collection(toList()).forEach(System.out::println); //3.flatmap,扁平化处理,flatMap[<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper);] 简而言之,传入一个Function,返回一个Stream //需求:一个字符串数组,借助stream去除其中的重复字符 String[] words = {"hello","world"}; Stream<String[]> stream = Arrays.stream(words).map(w -> w.split(""));//1.数组中的每个元素字符串变为字符数组,Stream<String[]> //可以通过以下输出测试,结果是helloword //Arrays.stream(words).map(w -> w.split("")).collect(toList()).forEach(a-> Arrays.stream(a).forEach(System.out::print)); //扁平化处理{H,e,l,l,o,W,o,r,l,d} Stream<String> streamStream = stream.flatMap(Arrays::stream); //2.传入数组流,处理其中的元素,传出Stream字符串流 streamStream.distinct().forEach(System.out::print); //去重,得Heldword
-
find、match、reduce方法:
//1.match方法,用来过滤 Stream<Integer> stream = Arrays.stream(new Integer[]{1,2,3,4,5,6,7}); //1.1 过滤大于0的元素 //allMatch:每个元素都满足传入的Predicate条件,Predicate:boolean test(T t); boolean matched = stream.allMatch(i -> i>0); assert matched : "some elements not matched"; stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7}); //1.2 存在一个大于6,anyMatch matched = stream.anyMatch(integer -> integer>6); System.out.println(matched); stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7}); //1.3 没有存在小于0的,noneMatch matched = stream.noneMatch(integer -> integer<0); System.out.println(matched); //2.find:返回值为Optional类型 Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7}); Optional<Integer> optional0 = stream.filter(i -> i % 2 == 0).findAny(); //2.1 根据条件查找某个值,打印存在的满足条件的第一个值 stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7}); Optional<Integer> optional1 = stream.filter(i -> i < 10).findAny(); System.out.println(optional1.get()); //打印满足条件的数据,1 //2.2 查询条件为空时,常用来解决空指针异常 stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7}); Optional<Integer> optional3 = stream.filter(i -> i > 10).findAny(); System.out.println(optional3.orElse(-1)); //找不到返回默认值-1 //类似于:根据某个条件去找,满足条件的返回,未满足返回默认值 int result = find(new Integer[]{1, 2, 3, 4, 5, 6, 7}, -1, i -> i > 100); System.out.println(result); private static int find(Integer [] values, int defdefaultValue, Predicate<Integer> predicate){ for (Integer value : values) { if(predicate.test(value)){ return value; } } return defdefaultValue; } //2.3判空操作 stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7}); Optional<Integer> optional4 = stream.filter(i -> i%2 == 0).findFirst(); option4.isPresent(); //判空操作,存在返回true,不存在,返回false; option4.ifPresent(System.out::println); //ifPresent(Consumer),判断结果为非空时,执行某个操作。 //filter再一次过滤,得到Functional System.out.print(option4.filter(i->i==2).get()); //public Optional<T> filter(Predicate<? super T> predicate)... //2.4传入自定义的Supplier option4.orElseGet(传入自定义的supplier); //默认值为自定义的supplier,注:Supplier<T>接口,涉及方法:T get(); stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7}); Optional<Integer> optional5 = stream.filter(i -> i>8).findFirst(); optional5.orElseGet(new customSupplier()); static class customSupplier implements Supplier<Integer>{ @Override public Integer get() { int i = new Random().nextInt(10); System.out.println("没有找到满足条件的值,返回一个默认值:"+i); return i; } } //其中的map方法,可以自定义一个optional,返回optional //3.reduce:聚合操作,类似元素的聚合操作,传入参数BiFunction,terminate类型的操作reduce,聚合作用根据你传入的Function进行对应的操作 Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7}); //3.1 打印所有元素的和操作 //T reduce(T identity, BinaryOperator<T> accumulator); //0是初始值,0+1+2+3+...+7 Integer result = stream.reduce(0,(i,j)->i+j); //等价于 Integer result = stream.reduce(0,Integer::sum); System.out.print(result); //3.2不给初始值,reduce操作的返回值为Optional,即Optional<T> reduce(BinaryOperator<T> accumulator); stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7}); //计算,如果结果存在,输出它,Optional<T> reduce(BinaryOperator<T> accumulator); stream.reduce((i, j) -> i + j).ifPresent(System.out::println); //3.3取最大值 stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7}); stream.reduce((i,j) -> { return i > j? i : j; }).ifPresent(System.out::println); //等价于 stream.reduce(Integer::max).ifPresent(System.out::println); //3.4偶数相乘,先过滤,在相乘 Integer reduce = stream.filter(i -> i%2 == 0).reduce(1,(a,b)->a*b); System.out.print(reduce); //通过optional继续操作 Optional.of(reduce).ifPresent(System.out::println);
-
常见的基本数据类型的操作:Numeric streams
Stream<Integer> stream = Arrays.stream(new Integer[]{1,2,3,4,5,6,7}); //需求1:filter >3 and get sum Stream<Integer> integerStream = stream.filter(i->i.intValue()>3); integerStream.reduce(0,Integer::sum).ifPresent(System.out::println); //或:stream.filter(i->i.intValue()>3).reduce(0,Integer::sum).ifPresent(System.out::println); //或:stream.filter(i->i.intValue()>3).reduce(Integer::sum).ifPresent(System.out::println); //存在的问题: //Integer的内存占用量大,int(4byte/32bit),比Integer内存占用量小,java提供了IntegerStream,方便使用拆箱后的数据 //解决方法: //将Integer转为int,使用mapToInt,需要传入参数ToIntFunction //Integer result = stream.filter(i -> i.intValue() > 3).reduce(0, Integer::sum); int sum = stream.mapToInt(Integer::intValue).filter(i -> i > 3).sum(); //int reduce = intStream.filter(i -> i > 3).reduce(0, (a, b) -> a + b); //int类型转为Integer,即装箱操作 //给定a=9,在1-1000范围内找到与a结合满足勾股定理的数,并放入到数组中result[1,2,3...] //类似切片,产生1-100的intStream int a = 9; /*IntStream intStream = IntStream.rangeClosed(1, 100) .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0); intStream.forEach(System.out::println);*/ //boxed装箱为对象,利用对象,map(Function<? super T, ? extends R> mapper) //map(Function<? super T, ? extends R> mapper) IntStream.rangeClosed(1, 100) .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0) .boxed().map(x->new int[]{a,x,(int)Math.sqrt(a*a+x*x)}) .forEach(r->System.out.println("a="+r[0]+", b="+r[1]+", c="+r[2])); //或直接转为对应的对象操作,省略装箱 IntStream.rangeClosed(1, 100) .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0) .mapToObj(b->new int[]{a,b,(int)Math.sqrt(a*a+b*b)}) .forEach(r->System.out.println("a="+r[0]+", b="+r[1]+", c="+r[2]));
-
-
Stream流练习
//数据准备: @Data @Builder @ToString public class Trader { private final String name; private final String city; public Trader(String n,String c){ this.name = n; this.city = c; } } @Data @Builder @ToString public class Transaction { private final Trader trader; private final int year; private final int value; public Transaction(Trader trader, int year, int value) { this.trader = trader; this.year = year; this.value = value; } public Trader getTrader(){ return this.trader; } } public class StreamInAction { public static void main(String[] args) { Trader raoul = Trader.builder().name("Raoul").city("Cambridge").build(); Trader mario = Trader.builder().name("Mario").city("Milan").build(); Trader alan = Trader.builder().name("Alan").city("Cambridge").build(); Trader brian = Trader.builder().name("Brian").city("Cambridge").build(); List<Transaction> transactions = Arrays.asList( Transaction.builder().trader(brian).year(2011).value(300).build(), Transaction.builder().trader(raoul).year(2012).value(1000).build(), Transaction.builder().trader(raoul).year(2011).value(400).build(), Transaction.builder().trader(mario).year(2012).value(710).build(), Transaction.builder().trader(mario).year(2012).value(700).build(), Transaction.builder().trader(alan).year(2012).value(950).build() ); //1.获取所有在2011年的交易,并且按照交易的量由小到大对它们进行排序 /*List<Transaction> result = transactions.stream().filter(transaction -> transaction.getYear() == 2011) .sorted(Comparator.comparing(Transaction::getValue)) .collect(toList()); System.out.println(result);*/ //2.在贸易产品中,那些是唯一的城市? /*transactions.stream().map(t->t.getTrader().getCity()) .distinct().forEach(System.out::println);*/ //3.查找出所有来自于Cambridge的交易员,并按照名字对他们进行排序 /*transactions.stream().map(Transaction::getTrader) .filter(trader -> trader.getCity().equals("Cambridge")) .distinct() .sorted(Comparator.comparing(Trader::getName)) .forEach(System.out::println);*/ //4.按照ascII码排序,返回所有交易员的名字 String value = transactions.stream().map(transaction -> transaction.getTrader().getName()) .distinct() .sorted() .reduce("", (name1, name2) -> name1 + name2); System.out.println(value); //5.是否存在来自Milan的交易员 boolean liveInMilan1 = transactions.stream().anyMatch(t -> t.getTrader().getCity().equals("Milan")); boolean liveInMilan2 = transactions.stream().map(Transaction::getTrader).anyMatch(t -> t.getCity().equals("Milan")); System.out.println(liveInMilan1); System.out.println(liveInMilan2); //6.打印出所有来自于Cambridge交易员的商品的值 transactions.stream() .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge")) .map(Transaction::getValue) .forEach(System.out::println); //7.求所有商品中最高价值的产品 Optional<Integer> maxValue = transactions.stream().map(Transaction::getValue).reduce(Integer::max);//(i,j)->i>j?i:j System.out.println(maxValue.get()); //8.找出所有商品中价格最低的商品 Optional<Integer> minValue = transactions.stream().map(Transaction::getValue).reduce(Integer::min); System.out.println(minValue.get()); } }