注解&&Lambda表达式

标签: 注解  lambda表达式  java

一、注解

Java 注解(Annotation)/java 标注;用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。

日常开发中新建Java类,我们使用class、interface比较多,而注解和它们一样,也是一种类的类型,他是用的修饰符为 @interface

在这里插入图片描述

1、注解基础知识

注解的定义

**注释:**文字描述介绍程序,提供给程序员查看的帮助信息。

**注解:**也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释

概念描述:

  1. 1.5之后新特性
  2. 解释说明程序。
  3. 注解使用:@注解

作用分类:

  1. **编写文档:**通过代码里标识的元数据/注解生成文档【生成文档doc文档/java API
  2. **代码分析:**通过代码里标识的元数据/注解对代码进行分析【使用反射
  3. **编译检查:**通过代码里标识的元数据/注解让编译器能够实现基本的编译检查【Override

2、内置注解

2.1、@Override

检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误

@Override
public String toString() {
	return "AnnoTest{}";
}

2.2、@Deprecated

标记过时方法。如果使用该方法,会报编译警告。如:Date

@Deprecated
public  void test1(){
	// 过时方法
    Date d = new Date();
	d.setYear(123);
}

2.3、@SuppressWarnings

指示编译器去忽略注解中声明的警告。一般参数为:all 即就是 @SuppressWarnings("all")

@SuppressWarnings("all")
public  void demo() {
	Date d = new Date();
	d.setYear(123);
}
@SuppressWarnings("all")
public class AnnoTest 

3、自定义注解

3.1、定义格式:

@元注解
public @interface 注解名称{
    属性;
}

在dos命令下编译反编译

3.2、注解的本质:

public interface MyAnno extends java.lang.annotation.Annotation {}
// 本质上就是一个接口默认继承Annotation

在这里插入图片描述

3.3、注解属性

接口中定义的成员抽象方法。要求:

  1. 属性的返回值类型有要求:1,基本类型 2,String 3,枚举 4,注解 5,以上类型的数组
  2. 定义了属性在使用时需要给属性赋值:1,使用default 有初始值可以不赋值。2,如果只有一个属性需要赋值,并且为value,value可以省略。3,数组赋值用{},只有一个值{}可以省略
// 定义一周七天的枚举类型
public enum WeekDayEnum { Mon, Tue, Wed, Thu, Fri, Sat, Sun, }
public static void main(String[] args) {
    WeekDayEnum day = null;
    switch (day) {
        case Mon:
            break;
        case Mon2:
            break;
    }
}

3.4、元注解

描述注解的注解

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})// MyAnno3注解只能作用于类上
@Retention(RetentionPolicy.RUNTIME)// 保留到class字节码中,被JVM读取。
@Documented// 生成api文档
@Inherited// 会被继承
public @interface MyAnno3 {
}@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

分类:

  • @Target - 标记这个注解应该是哪种 Java 成员。

    *ElementType:*TYPE 类上 METHOD 方法上 FIELD 子成员变量

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。

    RetentionPolicyRUNTIME 会保留到class字节码中,被JVM读取。 CLASS 也会保留到class字节码中,但不被JVM读取。SOURCE

  • @Documented - 标记这些注解是否包含在用户文档api中。

  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)。

4、程序中使用(解析)注解

获取注解中定义的属性值

/**
 * 描述执行的类名和方法名
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    String className();

    String methodName();
}
/**
 * public class ProImple implements Pro{
 *     String className(){
 *         return demo2.Demo1;
 *     }
 *     String methodName(){
 *         return test;
 *     }
 * }
 */

反射注解

@Pro(className = "demo2.Demo1",methodName = "test")
public class ReflectTest {
    public static void main(String[] args) throws Exception{
        // 解析注解
        // 1.获取字节码对象
        Class<ReflectTest> rtc = ReflectTest.class;
        // 2.获取注解
        Pro annotation = rtc.getAnnotation(Pro.class);// 在内存中生成注解接口的子类实现对象
        // 3.调用方法
        String s = annotation.className();
        String s1 = annotation.methodName();
        System.out.println(s);
        System.out.println(s1);
        // 反射
        Class<?> cls = Class.forName(s);// 加载class文件
        Object obj = cls.newInstance();// 构造对象
        Method method = cls.getMethod(s1);// 找到指定方法
        method.invoke(obj);// 运行方法
    }
}

Demo1

public class Demo1 {
    public void test(){
        System.out.println("Demo1...test...");
    }
}

流程:

  1. 获取注解定义位置的对象。
  2. 获取指定注解。getAnnotation()
  3. 调用注解的抽象方法,获取属性值。

5、综合案例

自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {}

测试对象

public class Calculator {
    // +
    @Check
    public void add() {
        String name = null;
        System.out.println(name.equals(123));
        System.out.println("1 + 0 =" + (1 + 0));
    }
    // -
    @Check
    public void sub() {
        System.out.println("1 - 0 =" + (1 - 0));
    }
    // *
    @Check
    public void mul() {
        System.out.println("1 × 0 =" + (1 * 0));
    }
    // /
    @Check
    public void div() {
        System.out.println("1 ÷ 0 =" + (1 / 0));
    }

    public void show(){
        System.out.println("没有 BUG");
    }
}

测试用例

/**
 * 测试框架
 * 主方法执行主动监测带 @Check 的方法,有异常记录
 */
public class TestCheck {
    public static void main(String[] args) throws Exception{
        // 1.获取计算机
        Calculator c = new Calculator();
        // 2.获取字节码文件对象
        Class cls = c.getClass();
        // 3.获取执行方法
        Method[] methods = cls.getMethods();
        int count = 0; // 异常记录
        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));

        // 4.判断有 @Check 并执行
        for (Method method : methods) {
            if (method.isAnnotationPresent(Check.class)) {
                try {
                    method.invoke(c);
                } catch (Exception e) {
                    // 5.捕获异常
                    // e.printStackTrace();
                    // 6.记录
                    count++;
                    bw.write(method.getName()+"方法异常了!");
                    bw.newLine();// 换行
                    bw.write("异常名称:"+e.getCause().getClass().getSimpleName());// 异常原因对象
                    bw.newLine();// 换行
                    bw.write("异常原因"+e.getCause().getMessage());
                    bw.newLine();// 换行
                    bw.write("------------------------------------------");
                    bw.newLine();// 换行
                }
            }
        }
        bw.write("本次测试一共出现异常"+count+"次");

        bw.flush();
        bw.close();
    }
}

6、后记

  1. 以后经常用注解但是不会定义注解。
  2. 注解器给编译器和解析程序用。
  3. 注解不是程序一部分。

二、Lambda 表达式

// Lambda 表达式
public class LambdaDemo1 {
    // Printer接口
    interface Printer {
        void printer(String val);
    }
    // 其他方法
    public void pringSomething(String someString, Printer printer) {
        printer.printer(someString);
    }
    public static void main(String[] args) {
        LambdaDemo1 lambdaDemo1 = new LambdaDemo1();
//        Printer printer = new Printer() {
//            public void printer(String val) {
//                System.out.println(val);
//            }
//        };
        Printer printer = (String val) -> System.out.println(val);
        lambdaDemo1.pringSomething("dadaisjuwg",printer);
    }
}

Lambda 表达式是一种匿名函数(对 Java 而言这并不完全正确,但现在姑且这么认为),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。

你可以将其想做一种速记,在你需要使用某个方法的地方写上它。当某个方法只使用一次,而且定义很简短,使用这种速记替代之尤其有效,这样,你就不必在类中费力写声明与方法了。

1、Lambda 表达式概述

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

lambda 表达式的语法格式如下:

(parameters参数) -> expression 表达式
或
(parameters) ->{ statements声明; }

以下是lambda表达式的重要特征:

  • **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号
  • **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda 表达式的结构

  • 一个 Lambda 表达式可以有零个或多个参数
  • 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)(a)效果相同
  • 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b)(int a, int b)(String a, int b, float c)
  • 空圆括号代表参数集为空。例如:() -> 42
  • 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
  • Lambda 表达式的主体可包含零条多条语句。
  • 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致。
  • 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空

2、Lambda 表达式实例

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

在 Java8Tester.java 文件输入以下代码:

public class Java8Tester {
   public static void main(String args[]){
      Java8Tester tester = new Java8Tester();
        
      // 类型声明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用类型声明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括号中的返回语句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 没有大括号及返回语句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括号
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括号
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }
}

使用 Lambda 表达式需要注意以下两点:

  • Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

3、变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

在 Java8Tester.java 文件输入以下代码:

public class Java8Tester {
 
   final static String salutation = "Hello! ";
   
   public static void main(String args[]){
      GreetingService greetService1 = message -> 
      System.out.println(salutation + message);
      greetService1.sayMessage("Runoob");
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
}

我们也可以直接在 lambda 表达式中访问外层的局部变量:

public class Java8Tester {
    public static void main(String args[]) {
        final int num = 1;
        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
        s.convert(2);  // 输出结果为 3
    }
    public interface Converter<T1, T2> {
        void convert(int i);
    }
}

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//报错信息:Local variable num defined in an enclosing scope must be final or effectively final

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错 

4、函数式接口

在 Java 中,Marker(标记)类型的接口是一种没有方法或属性声明的接口,简单地说,marker 接口是空接口。相似地,函数式接口是只包含一个抽象方法声明的接口。

java.lang.Runnable 就是一种函数式接口,在 Runnable 接口中只声明了一个方法 void run(),相似地,ActionListener 接口也是一种函数式接口,我们使用匿名内部类来实例化函数式接口的对象,有了 Lambda 表达式,这一方式可以得到简化。

每个 Lambda 表达式都能隐式地赋值给函数式接口,例如,我们可以通过 Lambda 表达式创建 Runnable 接口的引用。

Runnable r = () -> System.out.println("hello world");

当不指明函数式接口时,编译器会自动解释这种转化:

new Thread(
   () -> System.out.println("hello world")
).start();

因此,在上面的代码中,编译器会自动推断:根据线程类的构造函数签名 public Thread(Runnable r) { },将该 Lambda 表达式赋给 Runnable 接口。

以下是一些 Lambda 表达式及其函数式接口:

Consumer<Integer>  c = (int x) -> { System.out.println(x) };

BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);

Predicate<String> p = (String s) -> { s == null };

函数式接口只能有一个抽象方法,如果你尝试添加第二个抽象方法,将抛出编译时错误。

5、常用技巧

多线程

//旧方法:
new Thread(new Runnable() {
@Override
public void run() {
    System.out.println("Hello from thread");
}
}).start();

//新方法:
new Thread(
() -> System.out.println("Hello from thread")
).start();

以下代码的作用是打印出给定数组中的所有元素。注意,使用 Lambda 表达式的方法不止一种。在下面的例子中,我们先是用常用的箭头语法创建 Lambda 表达式,之后,使用 Java 8 全新的双冒号(::)操作符将一个常规方法转化为 Lambda 表达式:

//Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
   System.out.println(n);
}

//New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));

//or we can use :: double colon operator in Java 8
list.forEach(System.out::println);

在下面的例子中,我们使用断言(Predicate)函数式接口创建一个测试,并打印所有通过测试的元素,这样,你就可以使用 Lambda 表达式规定一些逻辑,并以此为基础有所作为:

package com.company;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class TT {

    public static void main(String [] a)  {

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

        System.out.println("Print all numbers:");
        evaluate(list, (n)->true);

        System.out.println("Print no numbers:");
        evaluate(list, (n)->false);

        System.out.println("Print even numbers:");
        evaluate(list, (n)-> n%2 == 0 );

        System.out.println("Print odd numbers:");
        evaluate(list, (n)-> n%2 == 1 );

        System.out.println("Print numbers greater than 5:");
        evaluate(list, (n)-> n > 5 );
    }
    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list)  {
            if(predicate.test(n)) {
                System.out.println(n + " ");
            }
        }
    }
}

三、Stream

java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+
public class LambdaDemo {
    public static void main(String[] args) {

        List<String> nameList = Arrays.asList("L张三", "J李四", "L王五", "J赵六");
        List<String> list = nameList.stream()// stream流
                .filter(s -> s.startsWith("L"))// 过滤
                .map(String::toUpperCase)// 调用String的toUpperCase
                .sorted() // 排序
                .collect(Collectors.toList());// 转成集合
        System.out.println(list);

        String[] obyList = {"L张三", "J李四", "L王五", "J赵六"};
        List<String> l = Stream.of(obyList).filter(s -> s.startsWith("J"))// 过滤
                .map(String::toLowerCase)// 调用String的toUpperCase
                .sorted() // 排序
                .collect(Collectors.toList());// 转成集合
        System.out.println(l);
    }
}

1、什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

2、生成流

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。
  • Stream.of() −内部调用了 Arrays.stream()
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

集合还可以调用 parallelStream() 方法创建并发流,默认使用的是 ForkJoinPool.commonPool()线程池。

List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();

3、操作流

3.1、forEach

Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数;

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

List<String> nameList = Arrays.asList("L张三", "J李四", "L王五", "J赵六");
        nameList.forEach(System.out::println);

3.2、过滤

通过 filter() 方法可以从流中筛选出我们想要的元素。

public class FilterStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");
        Stream<String> stream = list.stream().filter(element -> element.contains("王"));
        stream.forEach(System.out::println);
    }
}

filter() 方法接收的是一个 Predicate(Java 8 新增的一个函数式接口,接受一个输入参数返回一个布尔值结果)类型的参数,因此,我们可以直接将一个 Lambda 表达式传递给该方法,比如 element -> element.contains("王") 就是筛选出带有“王”的字符串。

forEach() 方法接收的是一个 Consumer(Java 8 新增的一个函数式接口,接受一个输入参数并且无返回的操作)类型的参数,类名 :: 方法名是 Java 8 引入的新语法,System.out 返回 PrintStream 类,println 方法你应该知道是打印的。

stream.forEach(System.out::println); 相当于在 for 循环中打印,类似于下面的代码:

for (String s : strs) {
    System.out.println(s);
}

3.3、映射

如果想通过某种操作把一个流中的元素转化成新的流中的元素,可以使用 map() 方法。

public class MapStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");
        Stream<Integer> stream = list.stream().map(String::length);
        stream.forEach(System.out::println);
    }
}

map() 方法接收的是一个 Function(Java 8 新增的一个函数式接口,接受一个输入参数 T,返回一个结果 R)类型的参数,此时参数 为 String 类的 length 方法,也就是把 Stream<String> 的流转成一个 Stream<Integer> 的流。

3.4、匹配

Stream 类提供了三个方法可供进行元素匹配,它们分别是:

  • anyMatch(),只要有一个元素匹配传入的条件,就返回 true。
  • allMatch(),只有有一个元素不匹配传入的条件,就返回 false;如果全部匹配,则返回 true。
  • noneMatch(),只要有一个元素匹配传入的条件,就返回 false;如果全部匹配,则返回 true。
public class MatchStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");

        boolean  anyMatchFlag = list.stream().anyMatch(element -> element.contains("王"));
        boolean  allMatchFlag = list.stream().allMatch(element -> element.length() > 1);
        boolean  noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉"));
        System.out.println(anyMatchFlag);
        System.out.println(allMatchFlag);
        System.out.println(noneMatchFlag);
    }
}

因为“王力宏”以“王”字开头,所以 anyMatchFlag 应该为 true;因为“周杰伦”、“王力宏”、“陶喆”、“林俊杰”的字符串长度都大于 1,所以 allMatchFlag 为 true;因为 4 个字符串结尾都不是“沉”,所以 noneMatchFlag 为 true。

3.5、组合

reduce() 方法的主要作用是把 Stream 中的元素组合起来,它有两种用法:

  • Optional<T> reduce(BinaryOperator<T> accumulator)

没有起始值,只有一个参数,就是运算规则,此时返回 Optional

  • T reduce(T identity, BinaryOperator<T> accumulator)

有起始值,有运算规则,两个参数,此时返回的类型起始值类型一致。

public class ReduceStreamDemo {
    public static void main(String[] args) {
        Integer[] ints = {0, 1, 2, 3};
        List<Integer> list = Arrays.asList(ints);

        Optional<Integer> optional = list.stream().reduce((a, b) -> a + b);
        Optional<Integer> optional1 = list.stream().reduce(Integer::sum);
        System.out.println(optional.orElse(0));
        System.out.println(optional1.orElse(0));

        int reduce = list.stream().reduce(6, (a, b) -> a + b);
        System.out.println(reduce);
        int reduce1 = list.stream().reduce(6, Integer::sum);
        System.out.println(reduce1);
    }
}

运算规则可以是 Lambda 表达式(比如 (a, b) -> a + b),也可以是类名::方法名(比如 Integer::sum)。

3.6、转换流

既然可以把集合或者数组转成流,那么也应该有对应的方法,将流转换回去——collect() 方法就满足了这种需求。

public class CollectStreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");

        String[] strArray = list.stream().toArray(String[]::new);
        System.out.println(Arrays.toString(strArray));

        List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
        List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new));
        System.out.println(list1);
        System.out.println(list2);

        String str = list.stream().collect(Collectors.joining(", ")).toString();
        System.out.println(str);
    }
}

toArray() 方法可以将流转换成数组,你可能比较好奇的是 String[]::new,它是什么东东呢?来看一下 toArray() 方法的源码。

<A> A[] toArray(IntFunction<A[]> generator);

也就是说 String[]::new 是一个 IntFunction,一个可以产生所需的新数组的函数,可以通过反编译字节码看看它到底是什么:

String[] strArray = (String[])list.stream().toArray((x$0) -> {
    return new String[x$0];
});
System.out.println(Arrays.toString(strArray));

也就是相当于返回了一个指定长度的字符串数组。

当我们需要把一个集合按照某种规则转成另外一个集合的时候,就可以配套使用 map() 方法和 collect() 方法。

List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());

通过 stream() 方法创建集合的流后,再通过 map(String:length) 将其映射为字符串长度的一个新流,最后通过 collect() 方法将其转换成新的集合。

Collectors 是一个收集器的工具类,内置了一系列收集器实现,比如说 toList() 方法将元素收集到一个新的 java.util.List 中;比如说 toCollection() 方法将元素收集到一个新的 java.util.ArrayList 中;比如说 joining() 方法将元素收集到一个可以用分隔符指定的字符串中。

4、分类

在这里插入图片描述

无状态:指元素的处理不受之前元素的影响;

有状态:指该操作只有拿到所有元素之后才能继续下去。

非短路操作:指必须处理所有元素才能得到最终结果;

短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

版权声明:本文为qq_43310689原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43310689/article/details/109047630

智能推荐

idea基础–(7)–jsp页面Controller路径自动识别的问题“Cannot resolve controller URL ...”,Cannot resolve variable

idea之所以强大,就是强大的代码提示和联想功能,写起代码来简直不要太爽。但是这几天我发现在我的jsp页面中访问controller路径的时候不会自动提示了,对于这么严谨的我肯定要找出原因啊,哈哈。 最终效果:按住ctrl,同时点击左键会自动跳转到对应的controller代码块,爽。 需要同时满足的条件 JSP页面顶部包含如下代码: 在idea的项目设置中显示如下:  若显示的是spring a...

26_Python基础_继承

面向对象三大特性: 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 继承 实现代码的重用, 相同的代码不需要重复的编写 多态 不同的对象调用相同的方法,  产生不同的执行结果,  增加代码的灵活度 1.  单继承 1.1 概念 继承的概念:&...

循环

与任何程序设计语言一样Java利用条件语句与循环结构确定流程控制,一下总结一下Java中的循环语句: while do while for switch 对于golang来说: switch非常灵活。从第一个expr为true的case开始执行,如果case带有fallthrough,程序会继续执行下一条case,不会再判断下一条case的expr,如果之后的case都有fallthrough,d...

1638 统计只差一个字符的子串数目(动态规划)

1. 问题描述: 给你两个字符串 s 和 t ,请你找出 s 中的非空子串的数目,这些子串满足替换一个不同字符以后,是 t 串的子串。换言之,请你找到 s 和 t 串中恰好只有一个字符不同的子字符串对的数目。比方说, "computer" 和 "computation"...

websocket基本原理

HTTP中一个request只能有一个response。而且这个response也是被动的,不能主动发起 因此过去的服务端推送信息是通过客户端不停的轮询实现的 websocket是双向通信协议,提供了服务端主动推送信息的能力 需要客户端(浏览器)和服务端同时支持 如果经过代理的话,还需要代理支持,否则有些代理在长时间无通信时会自动切断连接 因此WS为了保证连接不被断掉,会发心跳 WebSocket...

猜你喜欢

mybatis+ehcache二级缓存

导入jar包 mapper.xml文件开启二级缓存 pojo类实现序列化接口 配置ehcache.xml 测试...

python+opencv实现图像拼接

任务 拍摄两张图片去除相同部分,拼接在一起 原图 结果 步骤 读取两张图片 使用sift检测关键点及描述因子 匹配关键点 处理并保存关键点 得到变换矩阵 图像变换并拼接 代码实现 扩展 这里对右边图像进行变换,右边变得模糊,可以修改代码对左边图像变换 这里只有两张图片拼接,可以封装实现多张图片拼接 可以修改代码实现上下图片的拼接...

python_sklearn机器学习算法系列之AdaBoost------人脸识别(PCA,决策树)

          注:在读本文之前建议读一下之前的一片文章python_sklearn机器学习算法系列之PCA(主成分分析)------人脸识别(k-NearestNeighbor,KNN)         本文主要目的是通过一个简单的小...

memmove函数与memcpy函数的模拟实现

memmove函数和memcpy函数都是在内存复制任意类型的,但是它俩也有区别。当源区域和目标区域有重复的,memmove函数会复制缓冲区重叠的部分,而memcpy相反,会报出未知错误。 下面给出两个函数的实现 首先,memmove函数。 实现的基本原理如下图。 具体代码如下: memcpy函数的实现很简单,就直接给出源代码了...