java8新特性(二)Stream流

java8新特性(二)Stream流

  • 由需求初识Stream

    1. 有一份菜单,需求在其中找出卡路里低于指定值的菜单,且按照指定顺序对菜单排序,要求仅输出满足条件的菜单的名字

      //菜单类
      @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 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流简介

    1. stream是一个升级版的java API,可用于操作集合、IO以及自定义的操作,且可以真正实现并行化处理。(Stream中的spliterator()实现,底层基于fork-join机制实现)。
    2. 相比集合操作而言,stream将对集合的操作封装在内部,而collection则是用户取出其中的元素,进行相应的操作。
  • Stream流相关知识点

    1. 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:所有的一系列操作都发生在内部。
    2. 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

    3. 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等等。

    4. 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流中涉及的方法

    1. 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[ Stream flatMap(Function> mapper);]
      ////简而言之,传入一个Function,返回一个Stream
      //需求:一个字符串数组,借助stream去除其中的重复字符
      String[] words = {"hello","world"};
      Stream<String[]> stream = Arrays.stream(words).map(w -> w.split(""));//1.数组中的每个元素字符串变为字符数组,Stream
      //可以通过以下输出测试,结果是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
      
    2. 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 filter(Predicate predicate)...
      //2.4传入自定义的Supplier
      option4.orElseGet(传入自定义的supplier);  //默认值为自定义的supplier,注:Supplier接口,涉及方法: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 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 reduce(BinaryOperator accumulator);
      stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
      //计算,如果结果存在,输出它,Optional reduce(BinaryOperator 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);
      
      
    3. 常见的基本数据类型的操作: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 mapper)
      //map(Function 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 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());
        }
    }
    

代码在这儿

你可能感兴趣的:(java8)