近况

好长时间没有更新博客了,这段时间中忙着找工作,忙着熟悉工作上的事,但这其实并不算理由吧
真正让我没有更新博客的原因是:我更加清楚的知道自己的方向并在为之努力,这种方向可能并不是
单纯技术上的学习,更多的是对项目流程上的把控和认识,之前实习时候带过我的凯谟哥说的没错
业务确实比技术更重要吧,以前的我专注技术提升,实际上在公司中学习的并不只有技术,以有涯对无涯,殆矣
当然我并不是鼓吹无需掌握技术,而是觉得应该有这方面的意识,开发方向的技术学习当然也需要学习,
但无需深究,养活一个公司的永远是订单,而不是内部使用多高明的框架。

近期目标:

  • 我需要尽快熟悉公司真正的业务流程,我现在所在的公司哪里都好,就是有一点不好就是
    项目上开发文档几乎没有,开发文档不仅仅包括api接口内容,开发文档应该包括对整体项目概述,
    对业务模块和模块间联系的说明和阐述,对数据库表和表之间的关联关系解释,对业务流程有说明和相应的概括和细化
    不然新人学习成本太大,培养熟悉公司业务的员工是一大难事,一旦老员工离职对于初创企业来说是一个不小的打击。
  • 8月开始学习关于软考中软件设计师的内容,软件中级并不算简单,考试时间在11月份,准备3个月时间,
    一定要拿下这场考试,关于考试的资料,考试应该学习的网课内容,我已经收集完毕了
  • 学习公司经常使用的技术栈,计算机开发中有非常多的技术,学习的内容非常多,我觉得还是应该跟从公司
    技术栈来学习更好,经常使用的技术优先度应该要更高,这样学习的不仅是基础使用,而是实际场景中的解决方案

言归正传,今天学习的内容是函数式接口和Stream流的使用,函数式接口之前在《聊聊多线程(二)》博客中提过
今天重新回顾一下,Stream流是我一直想要学习但没有学习的内容,因为学习过js中的map,filter函数后
我发现两者真的很像,想来基础使用并不难,便一直拖到现在。下面是本章内容

函数式接口

  • 函数式接口的概念
    函数式接口指的是只有一个抽象方法的接口,例如以前学习的Runnable接口,Callable接口,Comparator接口
    下面是java8之前函数式接口:

    而在java8又新增了一些新的函数式接口,它们都在`java.util.function`包下, 主要有下面这些函数式接口,当然还有这些函数式接口的衍生接口就省略了
    1,Consumer<T>
    代表了接受一个输入参数并且无返回的操作
    
    2,Function<T,R>
    接受一个输入参数,返回一个结果。
    
    3,Predicate<T>
    接受一个输入参数,返回一个布尔值结果。
    
    4,Supplier<T>
    无参数,返回一个结果。
    
    5,UnaryOperator<T>
    接受一个参数为类型T,返回值类型也为T
  • 函数式接口的基本使用
    函数式接口一般当做参数使用,以往我们方法参数都是一些基本类型,包装类型,对象类型,
    那方法的参数可不可以是其他方法呢?当然也是可以的,举个例子

    //这里Thread对象在创建的时候,接受一个Runnable接口的实现类作为参数
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    });
    
    //你也可以将匿名实现类改写成lambda的形式,你看,这像不像是run方法作为了参数
    new Thread(()->{
        System.out.println(Thread.currentThread().getName());
    });
    
    //同样是使用lambda,我们也可以这么写,上面案例只是没特别说Thread参数需要是Runnable接口类型
    //这里把lambda所表示的类型写出来,表明,这个lambda表达式表示Runnable类型的
    Runnable runnable = ()->{
        System.out.println(Thread.currentThread().getName());
    };
    new Thread(runnable);
    
    //同样的,forEach方法参数可以接收一个Consumer接口实现类作为参数,而匿名Consumer接口实现类
    //可以使用lambda代替使用,当然这里也没有说明foreach要的是什么参数类型
    List list =Arrays.asList(1,2,3,4,5);
    list.forEach((num)->{
        System.out.println("你是:"+num);
    });
    
    //我们同样可以显示把接口类型写出来,不过常用的方式是上面这种方式
    List list =Arrays.asList(1,2,3,4,5);
    Consumer consumer = (num)->{
        System.out.println("你是:"+num);
    };
    list.forEach(consumer);
    
    /*
      看到这你应该知道了,其实函数式接口不管是Runnable接口还是Consumer接口
      通常都使用lambda方式表示,所以看起来就像是方法作为参数一样
      函数式最主要的特点是:函数式接口的功能实现由调用者实现,而不是编写者,
      例如线程池的拒绝策略就是由调用者实现而不是编写者。这里我们使用线程池是调用者,
      写线程池代码的人就是编写者
      下面同样是一个comsumer的简单使用,可能例子不恰当
    */
    public class Test {
        public static void main(String[] args) {
            //我调用方法,我自己实现如何处理每一个数据
            consumerTest((num)->{
                System.out.println("第"+num);
            });
        }
    
        public static void consumerTest(Consumer<Integer> consumer){
            List list =Arrays.asList(1,2,3,4,5);
            list.forEach(consumer);
        }
    }
  • 相关博客
    关于函数式接口的其他接口的具体使用,可以看看其他博客
    别人写的比我详细,我就不重复写了

Stream流

Stream流用来处理集合中的一些常见操作,例如去重,排序,过滤,遍历,查找,统计等等
因为是链式编程,数据像水流一样经过一个个操作处理,所以被叫做Stream流
因为网上已经有非常多的类似博客,所以我这里只是简单记录一下吧

Stream生命周期及各时期方法的使用

  • Stream生命周期或者说使用流程如下图:

  • 各个方法使用方式

    • stream(),Stream.of方法,Stream.iterate方法,Stream.generate方法,都可创建stream流

      //利用Stream.of方法创建流
      Stream<String> stream = Stream.of("hello", "world", "Java8");
      stream.forEach(System.out::println);
      System.out.println("##################");
      //利用Stream.iterate方法创建流
      Stream.iterate(10, n -> n + 1)
          .limit(5)
          .collect(Collectors.toList())
          .forEach(System.out::println);
      System.out.println("##################");
      //利用Stream.generate方法创建流
      Stream.generate(Math::random)
          .limit(5)
          .forEach(System.out::println);
      System.out.println("##################");
      //从现有的集合中创建流
      List<String> strings = Arrays.asList("hello", "world", "Java8");
      String string = strings.stream().collect(Collectors.joining(","));
      System.out.println(string);
    • filter(),map(),flatMap(),limit(n),skip(n),concat(),distinct(),sorted(),peek(),reduce,操作stream

      *************************************************************************
      //map()和flatMap,map用于一对一转换,flatMap用于一对多转换
      List<String> ids = Arrays.asList("205", "105", "308", "469", "627", "193", "111");
      // map
      List<User> results = ids.stream()
          .map(id -> {
              User user = new User();
              user.setId(id);
              return user;
          })
          .collect(Collectors.toList());
      /*
        2022-12-23更新
        1.如果没有collect方法,则map中对ids集合元素进行的操作不会对ids集合产生效果,
        2.如果collect方法返回results集合,则这个集合与ids不相等,至于是不是深拷贝不清楚
       	  并且源集合ids同样被修改了,ids集合数据与results集合数据保持一致。
       	3.使用map并不会打乱集合中元素的顺序,而parallelStream会打乱集合元素顺序
      */
      //flatmap,现有一个句子列表,需要将句子中每个单词都提取出来得到一个所有单词列表
      List<String> sentences = Arrays.asList("hello world","Jia Gou Wu Dao");
      List<String> results = sentences.stream()
          .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
          .collect(Collectors.toList());
      /*
        区别在于,map只能将流中元素转成其他类型,但不能将流中元素再次转为stream流
        flatmap,就是匹配流中每一个元素,还可对这些元素继续操作,将元素内部拆分出来
      */
      *************************************************************************
      //concat,两个流相连接用concat,三个以上用flatmap
      //两个流的连接
      Stream<String> first = Stream.of("sihai", "sihai2", "sihai3");
      Stream<String> second = Stream.of("sihai4", "sihai5", "sihai6");
      Stream<String> third = Stream.of("siha7", "sihai8", "sihai9");
      Stream<String> concat = Stream.concat(first, second);
      
      //多个流的连接
      Stream<String> stringStream = Stream.of(first, second, third).flatMap(Function.identity());
      *************************************************************************
      //limit,截取前几个流中元素
      //skip,跳过前几个流中元素
      //distinct,给流中元素去重
      //sorted,排序
      //filter,过滤,返回值为false过滤掉
      List<String> ids = Arrays.asList("205","10","308","49","627","193","111", "193");
      List<Dept> results = ids.stream()
          .filter(s -> s.length() > 2)
          .distinct()
          .map(Integer::valueOf)
          .sorted(Comparator.comparingInt(o -> o))
          .limit(3)
          .map(id -> new Dept(id))
          .collect(Collectors.toList());
      *************************************************************************
      /*
        peek,peek和foreach,都可以用于对元素进行遍历然后逐个的进行处理。
        但根据前面的介绍,peek属于中间方法,而foreach属于终止方法。这也就意味着peek只能作为管道中途
        的一个处理步骤,而没法直接执行得到结果,其后面必须还要有其它终止操作的时候才会被执行;
        而foreach作为无返回值的终止方法,则可以直接执行相关操作。
      */
      List<String> sentences = Arrays.asList("hello world","Jia Gou Wu Dao");
      // 演示点1: 仅peek操作,最终不会执行
      System.out.println("----before peek----");
      sentences.stream().peek(sentence -> System.out.println(sentence));
      System.out.println("----after peek----");
      // 演示点2: 仅foreach操作,最终会执行
      System.out.println("----before foreach----");
      sentences.stream().forEach(sentence -> System.out.println(sentence));
      System.out.println("----after foreach----");
      // 演示点3: peek操作后面增加终止操作,peek会执行
      System.out.println("----before peek and count----");
      sentences.stream().peek(sentence -> System.out.println(sentence)).count();
      System.out.println("----after peek and count----");
      //结果
      ----before peek----
      ----after peek----
      ----before foreach----
      hello world
      Jia Gou Wu Dao
      ----after foreach----
      ----before peek and count----
      hello world
      Jia Gou Wu Dao
      ----after peek and count----
      *************************************************************************
      //reduce,规约操作,和js中reduce一样,操作流中前后两个元素并将结果赋值给下一个元素,依次操作
      @Test
      public void testReduce2() {
          int sum = IntStream.range(1, 20)
              .reduce((x, y) -> x + y)
              .orElse(0);
          System.out.println(sum);
      
          int sum2 = IntStream.range(1, 20)
              .reduce(0, (x, y) -> x + 2 * y);
          System.out.println(sum2);
      
          int sum3 = IntStream.range(1, 20)
              .reduce(0, Integer::sum);
          System.out.println(sum3);
      }
      //结果
      190
      380
      190
    • count,max,min,summaryStatistics,findFirst,findAny,anyMatch,allMatch,noneMatch,collect,
      最后一次操作流的方法,终止Stream

      //count,统计stream操作后剩余的元素个数
      List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
      System.out.println(ids.stream().filter(s -> s.length() > 2).count());
      *************************************************************************
      //max,min,summaryStatistics
      List<Integer> integers = Arrays.asList(1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 2, 2, 2, 2);
      Optional<Integer> max1 = integers.stream().max(Integer::compare);
      Optional<Integer> max2 = integers.stream().min(Integer::compare);
      System.out.println(max1.get());
      System.out.println(max2.get());
      //反向排序
      Optional<Integer> var = list.stream().max(Comparator.reverseOrder()); 
      if (var.isPresent()) { 
          System.out.println(var.get()); 
      } 
      else { 
          System.out.println("-1"); 
      } 
      
      DoubleSummaryStatistics statistics = DoubleStream.generate(Math::random)
                      .limit(1000)
                      .summaryStatistics();
      *************************************************************************
      /*
        findFirst,findAny
        findFirst方法返回流中的第一个元素的Optional,而findAny方法返回流中的某个元素的 Optional。
      */
      String[] strings = {"hello", "sihai", "hello", "Java8"};
      Optional<String> first = Arrays.stream(strings)
          .findFirst();
      System.out.println(first.get());
      Optional<String> any = Arrays.stream(strings).findAny();
      System.out.println(any.get());
      *************************************************************************
      /*
        anyMatch,allMatch,noneMatch
        anyMatch(任何一个元素匹配,返回 true)
        allMatch(所有元素匹配,返回 true)
        noneMatch(没有一个元素匹配,返回 true)
      */
      boolean b = Stream.of(1, 2, 3, 4, 5, 10)
           .anyMatch(x -> x > 5);
      
      boolean b2 = Stream.of(1, 2, 3, 4, 5, 10)
          .allMatch(x -> x > 5);
      
      boolean b3 = Stream.of(1, 2, 3, 4, 5, 10)
          .noneMatch(x -> x > 5);
      *************************************************************************
      /*
        collect
        对集合数据的处理场景更多的是获取一个集合类的结果对象,比如List、Set或者HashMap等。
        这里就需要collect方法出场了,它可以支持生成如下类型的结果数据:
        1,一个集合类,比如List、Set或者HashMap等
        2,StringBuilder对象,支持将多个字符串进行拼接处理并输出拼接后结果
        3,一个可以记录个数或者计算总和的对象(数据批量运算统计)
      */
      //1,生成集合
      public void testCollectStopOptions() {
          List<Dept> ids = Arrays.asList(new Dept(17), new Dept(22), new Dept(23));
          // collect成list
          List<Dept> collectList = ids.stream().filter(dept -> dept.getId() > 20)
                  .collect(Collectors.toList());
          System.out.println("collectList:" + collectList);
          // collect成Set
          Set<Dept> collectSet = ids.stream().filter(dept -> dept.getId() > 20)
                  .collect(Collectors.toSet());
          System.out.println("collectSet:" + collectSet);
          // collect成HashMap,key为id,value为Dept对象
          Map<Integer, Dept> collectMap = ids.stream().filter(dept -> dept.getId() > 20)
                  .collect(Collectors.toMap(Dept::getId, dept -> dept));
          System.out.println("collectMap:" + collectMap);
      }
      //结果
      collectList:[Dept{id=22}, Dept{id=23}]
      collectSet:[Dept{id=23}, Dept{id=22}]
      collectMap:{22=Dept{id=22}, 23=Dept{id=23}}
      
      //2,生成拼接字符串,例如将一个List或者数组中的值拼接到一个字符串里并以逗号分隔开
      public void testCollectJoinStrings() {
          List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
          String joinResult = ids.stream().collect(Collectors.joining(","));
          System.out.println("拼接后:" + joinResult);
      }
      //结果,拼接后:205,10,308,49,627,193,111,193
      
      //3,数据批量数学运算,使用collect生成数字数据的总和信息
      public void testNumberCalculate() {
          List<Integer> ids = Arrays.asList(10, 20, 30, 40, 50);
          // 计算平均值
          Double average = ids.stream().collect(Collectors.averagingInt(value -> value));
          System.out.println("平均值:" + average);
          // 数据统计信息
          IntSummaryStatistics summary = ids.stream().collect(Collectors.summarizingInt(value -> value));
          System.out.println("数据统计信息: " + summary);
      }
      //结果
      平均值:30.0
      总和: IntSummaryStatistics{count=5, sum=150, min=10, average=30.000000, max=50}
  • 推荐文章