中间操作1之map、flatMap、peek

流中间操作在应用到流上,返回一个新的流。

  1. map

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    

    一个元素类型为 T 的流转换成元素类型为 R 的流,这个方法传入一个Function的函数式接口,接收一个泛型T,返回泛型R,map函数的定义,返回的流,表示的泛型是R对象

    //使用的People对象
    public class People {
        private String name;
        private int age;
        ...省略get,set方法
    }
    
    //将String转化为People对象
    Stream.of("小王:18","小杨:20").map(new Function<String, People>() {
         @Override
         public People apply(String s) {
             String[] str = s.split(":");
             People people = new People(str[0],Integer.valueOf(str[1]));
             return people;
         }
     }).forEach(people-> System.out.println("people = " + people));
    }
    
    //获取姓名
    List<People> peoples....
    peoples.stream.map(people -> people.getName()).collect(toList());
    peoples.stream.map(People::getName).collect(toList()); 
    
    //将每一个Employee的年龄增加一岁
    //将性别中的“M”换成“male”,F换成Female
    public static void main(String[] args){
    Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
    Employee e2 = new Employee(2,13,"F","Martina","Hengis");
    Employee e3 = new Employee(3,43,"M","Ricky","Martin");
    Employee e4 = new Employee(4,26,"M","Jon","Lowman");
    Employee e5 = new Employee(5,19,"F","Cristine","Maria");
    Employee e6 = new Employee(6,15,"M","David","Feezor");
    Employee e7 = new Employee(7,68,"F","Melissa","Roy");
    Employee e8 = new Employee(8,79,"M","Alex","Gussin");
    Employee e9 = new Employee(9,15,"F","Neetu","Singh");
    Employee e10 = new Employee(10,45,"M","Naveen","Jain");
    
    
    List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
    
    /*List<Employee> maped = employees.stream()
            .map(e -> {
                e.setAge(e.getAge() + 1);
                e.setGender(e.getGender().equals("M")?"male":"female");
                return e;
            }).collect(Collectors.toList());*/
    
    List<Employee> maped = employees.stream()
            .peek(e -> {
                e.setAge(e.getAge() + 1);
                e.setGender(e.getGender().equals("M")?"male":"female");
            }).collect(Collectors.toList());
    
    System.out.println(maped);
    
  2. flatMap

    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
    

    这个接口,跟map一样,接收一个Fucntion的函数式接口,不同的是,Function接收的第二个参数是一个Stream流;方法,返回的也是泛型R,具体的作用是把两个流,变成一个流返回

    map和flatMap的区别:我个人认为,flatMap的可以处理更深层次的数据,入参为多个list,结果可以返回为一个list,而map是一对一的,入参是多个list,结果返回必须是多个list。通俗的说,如果入参都是对象,那么flatMap可以操作对象里面的对象,而map只能操作第一层。

     // flatMap 提取 List<Students>  map 提取年龄
     List<Integer> ages = grades.stream().flatMap(grade -> grade.getStudents().stream()).map(Student::getAge).collect(Collectors.toList());
     
    //将“hello”,“world”两个字符串组成的集合,元素的每一个字母打印出来
     List<String> words = Arrays.asList("hello", "word");
     words.stream()
    .flatMap(w -> Arrays.stream(w.split(""))) // [h,e,l,l,o,w,o,r,l,d]
    .forEach(System.out::println);
    
    //分享一个flatMap的复杂操作,实现List<Data1>和List<Data2>根据Id进行连接,
    //将连接结果输出为一个List<OutputData>
    @Data
    @AllArgsConstructor
    public class Data1 {
        private int id;
        private String name;
        private int amount;
    }
     
    @Data
    @AllArgsConstructor
    public class Data2 {
        private int id;
        private String name;
        private String type;
    }
     
    @Data
    @AllArgsConstructor
    public class OutputData {
        private int id;
        private String name;
        private String type;
        private int amount;
    }
     
     
    @Test
    public void intersectByKeyTest(){
        List<Data2> listOfData2 = new ArrayList<Data2>();	 
        listOfData2.add(new Data2(10501, "JOE"  , "Type1"));
        listOfData2.add(new Data2(10603, "SAL"  , "Type5"));
        listOfData2.add(new Data2(40514, "PETER", "Type4"));
        listOfData2.add(new Data2(59562, "JIM"  , "Type2"));
        listOfData2.add(new Data2(29415, "BOB"  , "Type1"));
        listOfData2.add(new Data2(61812, "JOE"  , "Type9"));
        listOfData2.add(new Data2(98432, "JOE"  , "Type7"));
        listOfData2.add(new Data2(62556, "JEFF" , "Type1"));
        listOfData2.add(new Data2(10599, "TOM"  , "Type4"));
     
     
        List<Data1> listOfData1 = new ArrayList<Data1>(); 
        listOfData1.add(new Data1(10501, "JOE"    ,3000000));
        listOfData1.add(new Data1(10603, "SAL"    ,6225000));
        listOfData1.add(new Data1(40514, "PETER"  ,2005000));
        listOfData1.add(new Data1(59562, "JIM"    ,3000000));
        listOfData1.add(new Data1(29415, "BOB"    ,3000000));
     
        List<OutputData> result = listOfData1.stream()
                .flatMap(x -> listOfData2.stream()
                        .filter(y -> x.getId() == y.getId())
                        .map(y -> new OutputData(y.getId(), x.getName(), y.getType(), x.getAmount())))
                .collect(Collectors.toList());
        System.out.println(result);
    }
    
    
  3. peek
    java Stream<T> peek(Consumer<? super T> action)
    (1) Consumer是没有返回值的,它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
    (2) peek主要被用在debug用途。

    Stream.of("one", "two", "three","four").filter(e -> e.length() > 3)
                    .peek(e -> System.out.println("Filtered value: " + e))
                    .map(String::toUpperCase)
                    .peek(e -> System.out.println("Mapped value: " + e))
                    .collect(Collectors.toList());
      //输出:
       Filtered value: three
       Mapped value: THREE
       Filtered value: four
       Mapped value: FOUR             
    	
      //为什么只作为debug使用呢?我们再看一个例子:
      Stream.of("one", "two", "three","four").peek(u -> u.toUpperCase())
            .forEach(System.out::println);
      //输出:
      one
      two
      three
      four
     //可以看到stream中的元素并没有被转换成大写格式。
     
     //再看一个map的对比:
     Stream.of("one", "two", "three","four").map(u -> u.toUpperCase())
            .forEach(System.out::println);
      //输出:
      ONE
      TWO
      THREE
      FOUR
      //可以看到map是真正的对元素进行了转换。
    
      //当然peek也有例外,假如我们Stream里面是一个对象会怎么样?
       @Data
       @AllArgsConstructor
       static class User{
           private String name;
      }
       List<User> userList=Stream.of(new User("a"),new User("b"),new User("c"))
    											.peek(u->u.setName("kkk")).collect(Collectors.toList());
       log.info("{}",userList);
       //输出结果:
       10:25:59.784 [main] INFO com.flydean.PeekUsage - [PeekUsage.User(name=kkk), PeekUsage.User(name=kkk), 
    																PeekUsage.User(name=kkk)]
      //我们看到如果是对象的话,实际的结果会被改变。
    

    (3)结论
    我们看下peek和map的定义:

    Stream<T> peek(Consumer<? super T> action)
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    

    a. peek接收一个Consumer,而map接收一个Function。

    b. Consumer是没有返回值的,它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。

    c. 而Function是有返回值的,这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。

    d. 这就是为什么peek String不会发生变化而peek Object会发送变化的原因。

Reactor是一个基于Java 8的响应式编程框架,它提供了一种声明式和响应式的方式来处理异步流和事件流。在Reactor中,我们可以使用mapflatMap这两个操作符来对流进行转换和处理。 map操作符用于将流中的每个元素进行转换,生成一个新的元素。具体来说,它接收一个Function函数作为参数,该函数定义了元素的转换逻辑。map操作符应用于流中的每个元素,并将其转换为新的元素。例如,我们可以使用map操作符将流中的每个数字加倍: Flux<Integer> numbers = Flux.just(1, 2, 3, 4, 5); Flux<Integer> doubledNumbers = numbers.map(number -> number * 2); 在上面的例子中,map操作符将流中的每个数字乘以2,并生成一个新的流。 而flatMap操作符可以用于将流中的每个元素转换为一个新的流,并将所有新的流合并为一个流。具体来说,它接收一个Function函数作为参数,该函数定义了将元素转换为新的流的逻辑。flatMap操作符将流中的每个元素应用于该函数,得到一个新的流,然后将所有新的流合并为一个流。例如,我们可以使用flatMap操作符将流中的每个字符串分割为单词,并生成一个新的流: Flux<String> sentences = Flux.just("Hello World", "I am Reactor"); Flux<String> words = sentences.flatMap(sentence -> Flux.fromArray(sentence.split(" "))); 在上面的例子中,flatMap操作符将流中的每个字符串分割为单词,并生成一个包含所有单词的新的流。 总的来说,map操作符用于对流中的每个元素进行转换,生成一个新的元素,而flatMap操作符用于将流中的每个元素转换为一个新的流,并将所有新的流合并为一个流。这两个操作符是Reactor中常用的转换操作,可以帮助我们对异步流和事件流进行处理和转换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值