Java8新特性


Java8新特性

目录:

参考来源:

Interface修改

interface 的设计初衷是面向抽象,提高扩展性。这也留有一点遗憾,Interface 修改的时候,实现它的类也必须跟着改。

为了解决接口的修改与现有的实现不兼容的问题。新 interface 的方法可以用defaultstatic修饰,这样就可以有方法体,实现类也不必重写此方法。

一个 interface 中可以有多个方法被它们修饰,这 2 个修饰符的区别主要也是普通方法和静态方法的区别。

  1. default修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。
  2. static修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。

我们来看一个实际的例子。

public interface InterfaceNew {
    static void sm() {
        System.out.println("interface提供的方式实现");
    }
    static void sm2() {
        System.out.println("interface提供的方式实现");
    }

    default void def() {
        System.out.println("interface default方法");
    }
    default void def2() {
        System.out.println("interface default2方法");
    }
    //须要实现类重写
    void f();
}

public interface InterfaceNew1 {
    default void def() {
        System.out.println("InterfaceNew1 default方法");
    }
}

如果有一个类既实现了 InterfaceNew 接口又实现了 InterfaceNew1接口,它们都有def(),并且 InterfaceNew 接口和 InterfaceNew1接口没有继承关系的话,这时就必须重写def()。不然的话,编译的时候就会报错。

public class InterfaceNewImpl implements InterfaceNew , InterfaceNew1{
    public static void main(String[] args) {
        InterfaceNewImpl interfaceNew = new InterfaceNewImpl();
        interfaceNew.def();
    }

    @Override
    public void def() {
        InterfaceNew1.super.def();
    }

    @Override
    public void f() {
    }
}

在 Java 8 ,接口和抽象类有什么区别的?

很多小伙伴认为:“既然 interface 也可以有自己的方法实现,似乎和 abstract class 没多大区别了。”

其实它们还是有区别的

  1. interface 和 class 的区别,好像是废话,主要有:
    • 接口多实现,类单继承
    • 接口的方法是 public abstract 修饰,变量是 public static final 修饰。 abstract class 可以用其他修饰符
  2. interface 的方法是更像是一个扩展插件。而 abstract class 的方法是要继承的。

开始我们也提到,interface 新增defaultstatic修饰的方法,为了解决接口的修改与现有的实现不兼容的问题,并不是为了要替代abstract class。在使用上,该用 abstract class 的地方还是要用 abstract class,不要因为 interface 的新特性而将之替换。

记住接口永远和类不一样。

function interface 函数式接口

定义:也称 SAM 接口,即 Single Abstract Method interfaces,有且只有一个抽象方法,但可以有多个非抽象方法的接口。

在 java 8 中专门有一个包放函数式接口java.util.function,该包下的所有接口都有 @FunctionalInterface 注解,提供函数式编程。

在其他包中也有函数式接口,其中一些没有@FunctionalInterface 注解,但是只要符合函数式接口的定义就是函数式接口,与是否有

@FunctionalInterface注解无关,注解只是在编译时起到强制规范定义的作用。其在 Lambda 表达式中有广泛的应用。

Lambda表达式

众所周知的 Lambda 表达式,它是推动 Java 8 发布的最重要新特性。是继泛型(Generics)和注解(Annotation)以来最大的变化。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。让 java 也能支持简单的函数式编程

Lambda 表达式是一个匿名函数,java 8 允许把函数作为参数传递进方法中。

语法格式

(parameters) -> expression 或
(parameters) ->{ statements; }

替代匿名内部类

过去给方法传动态参数的唯一方法是使用内部类。比如

1.Runnable 接口

new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("The runable now is using!");
            }
}).start();
//用lambda
new Thread(() -> System.out.println("It's a lambda function!")).start();

2.Comparator 接口

List<Integer> strings = Arrays.asList(1, 2, 3);

Collections.sort(strings, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
    return o1 - o2;}
});

//Lambda
Collections.sort(strings, (Integer o1, Integer o2) -> o1 - o2);
//分解开
Comparator<Integer> comperator = (Integer o1, Integer o2) -> o1 - o2;
Collections.sort(strings, comperator);

3.Listener 接口

JButton button = new JButton();
button.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
   e.getItem();
}
});
//lambda
button.addItemListener(e -> e.getItem());

4.自定义接口

上面的 3 个例子是我们在开发过程中最常见的,从中也能体会到 Lambda 带来的便捷与清爽。它只保留实际用到的代码,把无用代码全部省略。那它对接口有没有要求呢?我们发现这些匿名内部类只重写了接口的一个方法,当然也只有一个方法须要重写。这就是我们上文提到的函数式接口,也就是说只要方法的参数是函数式接口都可以用 Lambda 表达式。

@FunctionalInterface
public interface Comparator<T>{}

@FunctionalInterface
public interface Runnable{}

我们自定义一个函数式接口

@FunctionalInterface
public interface LambdaInterface {
 void f();
}
//使用
public class LambdaClass {
    public static void forEg() {
        lambdaInterfaceDemo(()-> System.out.println("自定义函数式接口"));
    }
    //函数式接口参数
    static void lambdaInterfaceDemo(LambdaInterface i){
        System.out.println(i);
    }
}

集合迭代

void lamndaFor() {
        List<String> strings = Arrays.asList("1", "2", "3");
        //传统foreach
        for (String s : strings) {
            System.out.println(s);
        }
        //Lambda foreach
        strings.forEach((s) -> System.out.println(s));
        //or
        strings.forEach(System.out::println);
 				//map
        Map<Integer, String> map = new HashMap<>();
        map.forEach((k,v)->System.out.println(v));
}

方法的引用

Java 8 允许使用 :: 关键字来传递方法或者构造函数引用,无论如何,表达式返回的类型必须是 functional-interface。

public class LambdaClassSuper {
    LambdaInterface sf(){
        return null;
    }
}

public class LambdaClass extends LambdaClassSuper {
    public static LambdaInterface staticF() {
        return null;
    }

    public LambdaInterface f() {
        return null;
    }

    void show() {
        //1.调用静态函数,返回类型必须是functional-interface
        LambdaInterface t = LambdaClass::staticF;

        //2.实例方法调用
        LambdaClass lambdaClass = new LambdaClass();
        LambdaInterface lambdaInterface = lambdaClass::f;

        //3.超类上的方法调用
        LambdaInterface superf = super::sf;

        //4. 构造方法调用
        LambdaInterface tt = LambdaClassSuper::new;
    }
}

访问变量

int i = 0;
Collections.sort(strings, (Integer o1, Integer o2) -> o1 - i);
//i =3;

lambda 表达式可以引用外边变量,但是该变量默认拥有 final 属性,不能被修改,如果修改,编译时就报错。

Date-Time API

这是对java.util.Date强有力的补充,解决了 Date 类的大部分痛点:

  1. 非线程安全
  2. 时区处理麻烦
  3. 各种格式化、和时间计算繁琐
  4. 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。

我们从常用的时间实例来对比 java.util.Date 和新 Date 有什么区别。用java.util.Date的代码该改改了。

java.time 主要类

java.util.Date 既包含日期又包含时间,而 java.time 把它们进行了分离

LocalDateTime.class //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
LocalDate.class //日期 format: yyyy-MM-dd
LocalTime.class //时间 format: HH:mm:ss

格式化

Java 8 之前:

public void oldFormat(){
		Date now = new Date();
    //format yyyy-MM-dd HH:mm:ss
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    String date  = sdf.format(now);
    System.out.println(String.format("date format : %s", date));

    //format HH:mm:ss
    SimpleDateFormat sdft = new SimpleDateFormat("HH:mm:ss");
    String time = sdft.format(now);
    System.out.println(String.format("time format : %s", time));

    //format yyyy-MM-dd HH:mm:ss
    SimpleDateFormat sdfdt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String datetime = sdfdt.format(now);
    System.out.println(String.format("dateTime format : %s", datetime));
}

Java 8 之后:

public void newFormat(){
    //format yyyy-MM-dd
    LocalDate date = LocalDate.now();
    System.out.println(String.format("date format : %s", date));

    //format HH:mm:ss
    LocalTime time = LocalTime.now().withNano(0);
    System.out.println(String.format("time format : %s", time));

    //format yyyy-MM-dd HH:mm:ss
    LocalDateTime dateTime = LocalDateTime.now();
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String dateTimeStr = dateTime.format(dateTimeFormatter);
    System.out.println(String.format("dateTime format : %s", dateTimeStr));
}

字符串转日期格式

Java 8 之前:

//已弃用
Date date = new Date("2021-01-26");
//替换为
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = sdf.parse("2021-01-26");

Java 8 之后:

LocalDate date = LocalDate.of(2021, 1, 26);
LocalDate.parse("2021-01-26");

LocalDateTime dateTime = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
LocalDateTime.parse("2021-01-26 12:12:22");

LocalTime time = LocalTime.of(12, 12, 22);
LocalTime.parse("12:12:22");

Java 8 之前 转换都需要借助 SimpleDateFormat 类,而Java 8 之后只需要 LocalDateLocalTimeLocalDateTimeofparse 方法。

日期计算

下面仅以一周后日期为例,其他单位(年、月、日、1/2 日、时等等)大同小异。另外,这些单位都在 java.time.temporal.ChronoUnit 枚举中定义。

Java 8 之前:

public void afterDay(){
     //一周后的日期
     SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
     Calendar ca = Calendar.getInstance();
     ca.add(Calendar.DATE, 7);
     Date d = ca.getTime();
     String after = formatDate.format(d);
     System.out.println("一周后日期:" + after);

   //算两个日期间隔多少天,计算间隔多少年,多少月方法类似
     String dates1 = "2021-12-23";
   String dates2 = "2021-02-26";
     SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
     Date date1 = format.parse(dates1);
     Date date2 = format.parse(dates2);
     int day = (int) ((date1.getTime() - date2.getTime()) / (1000 * 3600 * 24));
     System.out.println(dates2 + "和" + dates2 + "相差" + day + "天");
     //结果:2021-12-23和2021-12-23相差300天
}

Java 8 之后:

public void pushWeek(){
     //一周后的日期
     LocalDate localDate = LocalDate.now();
     //方法1
     LocalDate after = localDate.plus(1, ChronoUnit.WEEKS);
     //方法2
     LocalDate after2 = localDate.plusWeeks(1);
     System.out.println("一周后日期:" + after);

     //算两个日期间隔多少天,计算间隔多少年,多少月
     LocalDate date1 = LocalDate.parse("2021-02-26");
     LocalDate date2 = LocalDate.parse("2021-12-23");
     Period period = Period.between(date1, date2);
     System.out.println("date1 到 date2 相隔:"
                + period.getYears() + "年"
                + period.getMonths() + "月"
                + period.getDays() + "天");
		 //打印结果是 “date1 到 date2 相隔:0年9月27天”
     //这里period.getDays()得到的天是抛去年月以外的天数,并不是总天数
     //如果要获取纯粹的总天数应该用下面的方法
     long day = date2.toEpochDay() - date1.toEpochDay();
     System.out.println(date2 + "和" + date2 + "相差" + day + "天");
     //打印结果:2021-12-23和2021-12-23相差300天
}

获取指定日期

除了日期计算繁琐,获取特定一个日期也很麻烦,比如获取本月最后一天,第一天。

Java 8 之前:

public void getDay() {

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        //获取当前月第一天:
        Calendar c = Calendar.getInstance();
        c.set(Calendar.DAY_OF_MONTH, 1);
        String first = format.format(c.getTime());
        System.out.println("first day:" + first);

        //获取当前月最后一天
        Calendar ca = Calendar.getInstance();
        ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
        String last = format.format(ca.getTime());
        System.out.println("last day:" + last);

        //当年最后一天
        Calendar currCal = Calendar.getInstance();
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, currCal.get(Calendar.YEAR));
        calendar.roll(Calendar.DAY_OF_YEAR, -1);
        Date time = calendar.getTime();
        System.out.println("last day:" + format.format(time));
}

Java 8 之后:

public void getDayNew() {
    LocalDate today = LocalDate.now();
    //获取当前月第一天:
    LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
    // 取本月最后一天
    LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
    //取下一天:
    LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
    //当年最后一天
    LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());
    //2021年最后一个周日,如果用Calendar是不得烦死。
    LocalDate lastMondayOf2021 = LocalDate.parse("2021-12-31").with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
}

java.time.temporal.TemporalAdjusters 里面还有很多便捷的算法,这里就不带大家看 Api 了,都很简单,看了秒懂。

JDBC 和 java8

现在 jdbc 时间类型和 java8 时间类型对应关系是

  1. Date —> LocalDate
  2. Time —> LocalTime
  3. Timestamp —> LocalDateTime

而之前统统对应 Date,也只有 Date

Stream API

概念

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

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

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

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

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

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

以上的流程转换为 Java 代码为:

List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();

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)实现。

获取流

  • 集合

    集合含有stream()方法来获取流

    default Stream stream();

  • 数组:

    • Arrays.stream(T[] array);

    • Stream.of(T… values); (可变参数可以接收数组作为参数)

// 1. 集合
Collection<String> list = new ArrayList<>();
Stream<String> ss = list.stream();

// 2. 数组
String[] arrs = new String[]{"Java", "Python" ,"C"};
Stream<String> arrs1 = Arrays.stream(arrs);
Stream<String> arrs2 = Stream.of(arrs);

Stream 操作分类

  • 中间操作(intermediate):通过一系列中间(Intermediate)方法,对数据集进行过滤、检索等数据集的再次处理。

    • 中间操作的特点是方法返回值还是 Stream 类型,可以继续链式调用其他方法执行操作。
    • 中间操作是对数据的加工,注意,中间操作是 lazy 操作,并不会立马启动,需要等待终止操作才会执行。
  • 终止操作(Terminal):通过最终(terminal)方法完成对数据集中元素的处理。

    • 终止操作的特点是方法返回值为 空 或者 一个其他类型的结果。
    • 终止操作是 Stream 的启动操作,只有加上终止操作,Stream才会真正的开始执行。

无状态(Stateless):指元素的处理不受之前元素的影响;
有状态(Stateful):指该操作只有拿到所有元素之后才能继续下去。
非短路操作(Unshort-circuiting):指必须处理所有元素才能得到最终结果;
短路操作(Short-circuiting):指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

img

中间操作

filter

【中间操作】过滤元素,筛选 Stream 流中符合条件的元素,作为流返回。

  • 定义:

    Stream<T> filter(Predicate<? super T> predicate);
  • 实例:找出集合中的空元素

    list.stream().filter(String::isEmpty).forEach(System.out::println);

map

【中间操作】对 Stream 流中的元素执行指定操作后映射为新的值流返回(会改变之前的集合元素),相当于加工。

  • 定义:

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
  • 实例:将集合中的元素变为大写

    list.stream().map(String::toUpperCase).forEach(System.out::println);

peek

【中间操作】返回一个由该流的元素组成的流,另外在每个元素上执行提供的操作,因为元素从结果流中被消耗。(Consumer 没有返回值,不会改变原来 Stream 流中的值)

  • 定义:

    Stream<T> peek(Consumer<? super T> action);
  • 实例:源码中的说明

    @apiNote This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline:
    此方法的存在主要是为了支持调试,您希望在其中查看元素流经管道中的某个点时的情况:

    list.stream()
        .filter(e -> e.length() > 3)
        .peek(e -> System.out.println("Filtered value: " + e))
        .map(String::toUpperCase)
        .peek(e -> System.out.println("Mapped value: " + e))
        .forEach(System.out::println);

limit

【短路有状态中间操作】截取 Stream 流中前 maxSize 个 元素。

  • 定义:

    Stream<T> limit(long maxSize);
  • 实例:取集合中前 4 个元素。

    list.stream().limit(4).forEach(System.out::println);

skip

【有状态中间操作】在丢弃流的前 n 元素后,返回由该流的其余元素组成的流。 如果此流包含少于 n 元素,则将返回一个空流。

  • 定义:

    Stream<T> skip(long n);
  • 实例:跳过集合中前 4 个元素。

    list.stream().skip(4).forEach(System.out::println);

distinct

【有状态中间操作】返回由该流的不同元素(根据Object.equals(Object) )组成的流。

对于有序流,不同元素的选择是稳定的(对于重复元素,保留遇到顺序中最先出现的元素。)
对于无序流,没有稳定性保证。

  • 定义:

    Stream<T> distinct();
  • 实例:去除集合中重复的元素。

    list.stream().distinct().forEach(System.out::println);

sorted

【有状态中间操作】返回由该流的元素组成的流,按自然顺序排序。

如果此流的元素不是 Comparable ,则在执行终端操作时可能会抛出java.lang.ClassCastException 。
对于有序流,排序是稳定的。 对于无序流,没有稳定性保证。

  • 定义:

    Stream<T> sorted();
    
    Stream<T> sorted(Comparator<? super T> comparator);
  • 实例:按照 自然排序/自然排序倒序 集合中的元素。

    list.stream().sorted().forEach(System.out::println);
    
    // 还可以实现 Comparator 接口来自定义排序规则
    list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);

终止操作

forEach

【终止操作】遍历操作,依次遍历 Stream 流中的元素,并执行给定的行为。

  • 定义:

    void forEach(Consumer<? super T> action);
  • 实例:遍历集合中的元素

    list.stream().forEach(System.out::println);
    
    // 集合只遍历可简写为 Collection 自带的 forEach() 方法
    // list.forEach(System.out::println);

toArray

【终止操作】返回一个包含此流元素的数组。

  • 定义:

    Object[] toArray();
  • 实例:将集合转成数组。

    list.stream().toArray();
    
    // 对于集合可简写为 Collection 自带的 toArray() 方法
    // list.toArray();

min/max

【终止操作】返回 Stream 流中最小/最大的元素

  • 定义:

    Optional<T> min(Comparator<? super T> comparator);
    
    Optional<T> max(Comparator<? super T> comparator);
  • 实例:返回集合中最小/最大的元素

    Optional<String> min = list.stream().min(Comparator.naturalOrder());
    System.out.println(min.get());
    
    Optional<String> max = list.stream().max(Comparator.naturalOrder());
    System.out.println(max.get());

count

【终止操作】返回此流中元素的计数。

  • 定义:

    long count();
  • 实例:统计集合中的元素个数

    long size = list.stream().count();
    
    // 对于集合可简写为 Collection 自带的 size() 方法
    // list.size();
    

reduce

【终止操作】使用提供的标识值和关联累积函数对该流的元素执行归约,并返回归约后的值。

  • 定义:

    T reduce(T identity, BinaryOperator<T> accumulator);
    
    Optional<T> reduce(BinaryOperator<T> accumulator);
    
    <U> U reduce(U identity,
                     BiFunction<U, ? super T, U> accumulator,
                     BinaryOperator<U> combiner);
  • 实例:将集合中的元素累加。

    String result = list.stream().reduce("", (s1, s2) -> s1 + s2); // JavaSpringMysqlPythonC++
    
    Optional<String> result = list.stream().reduce((s1, s2) -> s1 + s2);

collect

【终止操作】将 Stream 流转换为其他形式,该操作主要作为进行中间操作后的可变规约操作。

  • 定义:

    <R> R collect(Supplier<R> supplier,
                      BiConsumer<R, ? super T> accumulator,
                      BiConsumer<R, R> combiner);
    
    <R, A> R collect(Collector<? super T, A, R> collector);
  • 实例:将 List 转成 Set 。

    list.stream().collect(Collectors.toSet());
    
    // 连接字符串
    // String result = list.stream().collect(Collectors.joining("-"));

查找与匹配

anyMatch

【短路终止操作】如果流的任何元素与提供的 断言 匹配,则为 true ,否则为 false。

  • 定义:

    boolean anyMatch(Predicate<? super T> predicate);
  • 实例:判断集合中是否存在有长度大于 6 的元素。

    boolean result = list.stream().anyMatch(s -> s.length() > 6) // false

allMatch

【短路终止操作】Stream 流中所有元素都与提供的 断言匹配,则为 ture,否则为 false。

  • 定义:

    boolean allMatch(Predicate<? super T> predicate);
  • 实例:判断集合中的元素长度是否都小于等于 6。

    boolean result = list.stream().allMatch(s -> s.length() <= 6); // true

noneMatch

【短路终止操作】Stream 流中的所有元素都与提供的 断言 不匹配,则返回 true,否则为 false。

  • 定义:

    boolean noneMatch(Predicate<? super T> predicate);
  • 实例:判断集合中的元素长度是否都不满足大于 6。

    boolean result = list.stream().noneMatch(s -> s.length() > 6); // true

findFirst

【短路终止操作】返回 Stream 流中第一个元素。

  • 定义:

    Optional<T> findFirst();
  • 实例:

    Optional<String> first = list.stream().findFirst();
    String result = first.get(); // Java

findAny

【短路终止操作】返回 Stream 流中第一个元素。

此操作的行为明显是不确定的; 可以自由选择流中的任何元素。
这是为了在并行操作中实现最大性能;
代价是对同一源的多次调用可能不会返回相同的结果。 (如果需要稳定的结果,请改用findFirst() 。)

  • 定义:

    Optional<T> findAny();
  • 实例:

    Optional<String> first = list.stream().findAny();
    String result = first.get(); // Java
    
    Optional<String> first = list.parallelStream().findAny();
    String result = first.get(); // Python

更多参考:https://www.yuque.com/zhangshuaiyin/java/java-8-stream-api

一些好的示例

  • 遍历树形结构

    实体类:Menu.java

    /**
     * Menu
     *
     * @author lcry
     * @date 2020/06/01 20:36
     */
        @Data
        @Builder
        public class Menu {
        /**
         * id
         */
         public Integer id;
         /**
         * 名称
         */
         public String name;
         /**
         * 父id ,根节点为0
         */
         public Integer parentId;
         /**
         * 子节点信息
         */
         public List<Menu> childList;
    
    
        public Menu(Integer id, String name, Integer parentId) {
            this.id = id;
            this.name = name;
            this.parentId = parentId;
        }
        
        public Menu(Integer id, String name, Integer parentId, List<Menu> childList) {
            this.id = id;
            this.name = name;
            this.parentId = parentId;
            this.childList = childList;
        }
    
    }

    递归组装树形结构:

    @Test
    public void testtree(){
        //模拟从数据库查询出来
        List<Menu> menus = Arrays.asList(
                new Menu(1,"根节点",0),
                new Menu(2,"子节点1",1),
                new Menu(3,"子节点1.1",2),
                new Menu(4,"子节点1.2",2),
                new Menu(5,"根节点1.3",2),
                new Menu(6,"根节点2",1),
                new Menu(7,"根节点2.1",6),
                new Menu(8,"根节点2.2",6),
                new Menu(9,"根节点2.2.1",7),
                new Menu(10,"根节点2.2.2",7),
                new Menu(11,"根节点3",1),
                new Menu(12,"根节点3.1",11)
        );
    
        //获取父节点
        List<Menu> collect = menus.stream().filter(m -> m.getParentId() == 0).map(
                (m) -> {
                    m.setChildList(getChildrens(m, menus));
                    return m;
                }
        ).collect(Collectors.toList());
        System.out.println("-------转json输出结果-------");
        System.out.println(JSON.toJSON(collect));
    }
    
    /**
     * 递归查询子节点
     * @param root  根节点
     * @param all   所有节点
     * @return 根节点信息
     */
    private List<Menu> getChildrens(Menu root, List<Menu> all) {
        List<Menu> children = all.stream().filter(m -> {
            return Objects.equals(m.getParentId(), root.getId());
        }).map(
                (m) -> {
                    m.setChildList(getChildrens(m, all));
                    return m;
                }
        ).collect(Collectors.toList());
        return children;
    }

Supplier

Java Supplier是一个功能接口,代表结果的提供者。Supplier在Java 8中被引入,属于java.util.function包。

Supplier的功能方法是get()。Supplier功能接口的源代码如下。

@FunctionalInterface
public interface Supplier<T> {
    T get();
} 

我们可以看到在上面的代码中,Supplier有get()方法,可以返回通用类型的值。get()方法不接受任何参数,只返回通用类型的值。

一个Supplier可以通过lambda表达式、方法引用或默认构造函数来实例化。

public static void main(String[] args) {
  	 // lambda表达式
     Supplier<String> s1 = () -> "Hello World!"; 
     System.out.println(s1.get());

     // 方法引用.假设myUtil是MyUtil类的实例,getAge()是一个非静态方法
  	 MyUtil myUtil = new MyUtil();      
     Supplier<Integer> s2 = myUtil::getAge;
     System.out.println(s2.get()); 
  
  	 // 默认构造函数
  	 Supplier<Random> s3 = Random::new; 
     Random random = s3.get();
     System.out.println(random.nextInt(10));   
  }

Java还提供了返回特定类型值的Supplier。BooleanSupplier返回Boolean数据类型,IntSupplier返回Integer数据类型,LongSupplier返回Long数据类型,DoubleSupplier返回Double数据类型值。


文章作者: 小小千千
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小小千千 !
评论
  目录