java8完全解读二

继续着上次的java完全解读一

继续着上次的java完全解读一
1.强大的Stream API
1.1什么是Stream
1.2 Stream操作的三大步骤
1.2.1 创建Stream
1.2.2 Stream的中间操作
筛选和切片
映射
排序
1.2.3 Stream 的终止操作
查找与匹配
归约
收集
2 新时间日期API
2.1 使用LocalDate、LocalTime、LocalDateTime
2.2 Duration 和Period
2.3 解析与格式化
2.4 时区的处理
3 接口中的默认方法与静态方法
3.1 接口中的默认方法
3.1.1 默认方法的使用
3.1.2 接口默认方法的”类优先”原则
3.1.3 接口默认方法的"接口冲突"
3.2 接口中的静态方法

1.强大的Stream API

Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1.1什么是Stream

流(Stream)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

“集合讲的是数据,流讲的是计算!”

注意:

①Stream 自己不会存储元素。

②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

1.2 Stream操作的三大步骤

  • 创建Stream

    一个数据源(如:集合、数组),获取一个流

  • 中间操作

    一个中间操作链,对数据源的数据进行处理

  • 终止操作(终端操作)

    一个终止操作,执行中间操作链,并产生结果

1.2.1 创建Stream

创建Stream严格来说有三种方式

  • Java8 中的Collection 接口被扩展,提供了两个获取流的方法:
//Collection的方法
default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流
  • Java8 中的Arrays的静态方法stream() 可以获取数组流:
  • 可以使用静态方法Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数
//Arrays的方法
static <T> Stream<T> stream(T[] array);// 返回一个流
//Stream的方法
public static<T> Stream<T> of(T… values);// 返回一个流
  • 可以使用静态方法Stream.iterate()Stream.generate(), 创建无限流。
//迭代
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//生成
public static<T> Stream<T> generate(Supplier<T> s) :
/*
*UnaryOperator是传入类型T返回类型T
* Supplier是无参,返回类型T
*/

实际运用

public void test1(){
        /**
        * list中的数据本来是用List包裹,创建stream后,将list中的数据换成了stream包裹了,
        * 同时对流中的数据操作变得更加方便
        */
        //1. Collection 提供了两个方法  stream() 与 parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream(); //获取一个顺序流
        Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

        //2. 通过 Arrays 中的 stream() 获取一个数组流
        Integer[] nums = new Integer[10];
        Stream<Integer> stream1 = Arrays.stream(nums);
        //通过 Stream 类中静态方法 of()底层也是调用
        Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);

        //3.创建无限流,这两个底层调用的都是一样的
        //迭代
        Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
        stream3.forEach(System.out::println);
        //生成
        Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
        stream4.forEach(System.out::println);
    }

1.2.2 Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

筛选和切片

运用

//筛选年龄<=35岁的员工
@Test
public void test2(){
    //所有的中间操作不会做任何的处理
    Stream<Employee> stream = emps.stream()
            .filter((e) -> {
                System.out.println("测试中间操作");
                return e.getAge() <= 35;
         });
   //只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
   stream.forEach(System.out::println);
}

//查询薪水>=5000的前三个员工
@Test
public void test4(){
      emps.stream()
            .filter((e) -> {
             return e.getSal() >= 5000;
      }).limit(3)
            .forEach(System.out::println);
  }

//查询薪水>=5000的员工,去掉前两个
    @Test
    public void test5(){
        emps.stream()
                .filter((e) -> e.getSal()>= 5000)
                .skip(2)//跳过2元素,返回一个扔掉了前2个元素的流
                .forEach(System.out::println);
    }

//去重
@Test
public void test6(){
     emps.stream()
            .distinct()
            .forEach(System.out::println);
  }
映射

实例运用

  • map
@Test
    public void test1() {
        List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        //将传入的String值转为大写并返回String
        Stream<String> stream = strList.stream()
                .map(String::toUpperCase);
        stream.forEach(System.out::println);
        //传入的String,通过方法将String转化成包裹字符的stream了
        Stream<Stream<Character>> stream2 = strList.stream()
                .map(Stream2::filterCharacter);
        stream2.forEach((sm) -> {
            sm.forEach(System.out::println);
        });
    }
    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }
  • flatMap
//flatmap
    @Test
    public void Test3() {
        List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        Stream<Character> stream3 = strList.stream()
                .flatMap(Stream2::filterCharacter);
        stream3.forEach(System.out::println);
    }
排序

实际运用

@Test
    public void test3() {
        emps.stream()
                .map(Employee::getName)
                .sorted()
                .forEach(System.out::println);
        emps.stream()//按年龄排序,如果年龄相同:按名字排序
                .sorted((x, y) -> {
                    if (x.getAge() == y.getAge()) {
                        return x.getName().compareTo(y.getName());
                    } else {
                        return Integer.compare(x.getAge(), y.getAge());
                    }
                }).forEach(System.out::println);
    }

1.2.3 Stream 的终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。

查找与匹配

归约

备注:map 和reduce 的连接通常称为map-reduce 模式,因Google 用它来进行网络搜索而出名。

使用

    @Test
    public void test1(){
        //将数组中的值加起来
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);
        //使用map将员工的工资提取出来,使用reduce将工资累加
        Optional<Double> op = emps.stream()
                .map(Employee::getSal)
                .reduce(Double::sum);
        System.out.println(op.get());
    }
    //需求:搜索名字中 “六” 出现的次数
    @Test
    public void test2(){
        Optional<Integer> sum = emps.stream()
                .map(Employee::getName)
                .flatMap(Stream2::filterCharacter)
                .map((ch) -> {
                    if(ch.equals(‘六‘))
                        return 1;
                    else
                        return 0;
                }).reduce(Integer::sum);

        System.out.println(sum.get());
    }
收集

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

2 新时间日期API

2.1 使用LocalDate、LocalTime、LocalDateTime

LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

提供的方法:

运用

@Test
    public void test() {
        LocalDate localDate = LocalDate.now();//现在的日期
        LocalTime localTime = LocalTime.now();//现在的时间
        LocalDateTime localDateTime = LocalDateTime.now();//现在的时间和日期
        System.out.println(localDate+"==>"+localTime+"==>"+localDateTime);
    }
    @Test
    public void test2() {
        LocalDate localDate = LocalDate.of(1996, 02, 03);//指定现在的日期
        LocalTime localTime = LocalTime.of(16, 04);//指定现在的时间
        LocalDateTime localDateTime = LocalDateTime.of(1996, 02, 03, 16, 04);//指定现在的日期时间
        System.out.println(localDate+"==>"+localTime+"==>"+localDateTime);
    }
    @Test
    public void test3() {
        LocalDate localDate = LocalDate.now();
        LocalDate localDate1 = localDate.plusDays(5);//在当前时间上加5天
        LocalDate localDate2 = localDate.plusMonths(2);//加2个月
        LocalDate localDate3 = localDate.plusWeeks(1);//加一周
        LocalDate localDate4 = localDate.plusYears(1);//加一年
        LocalDate localDate5 = localDate.minusMonths(5);//减5天
        System.out.println(localDate4);
    }

    @Test
    public void test5() {
        LocalDate localDate = LocalDate.now();
        LocalDate localDate1 = localDate.withDayOfMonth(12);//将日期中的,这个月的天数修改为12
        LocalDate localDate2 = localDate.withYear(2015);//将日期中的年修改为2015
        LocalDate localDate3 = localDate.withMonth(8);//将日期中的月修改为8月
        System.out.println(localDate3);
    }
    @Test
    public void test6() {
        LocalDate localDate = LocalDate.now();
        int year = localDate.getYear();//获取当前的年份
        System.out.println(year);
    }
    @Test
    public void test7() {
        LocalDate localDate = LocalDate.now();
        LocalDate localDate1 = LocalDate.now();
        Period period = localDate.until(localDate1);//获取两个日期间的日期间隔
        boolean before = localDate.isBefore(localDate1);//比较两个localData
        boolean leapYear = localDate.isLeapYear();//是否是闰年
        System.out.println(before);
    }

2.2 Duration 和Period

  • Duration:用于计算两个“时间”间隔
  • Period:用于计算两个“日期”间隔

使用

@Test
    public void test4() {
        //时间戳
        Instant start = Instant.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        Instant end = Instant.now();
        //Duration:用于计算两个“时间”间隔
        Duration duration = Duration.between(start, end);
        // Period:用于计算两个“日期”间隔
        LocalDate localDate = LocalDate.now();
        LocalDate localDate1 = LocalDate.of(2017, 12, 1);
        Period period = Period.between(localDate, localDate1);
        //给日期加一个间隔
        LocalDate localDate3 = localDate.plus(period);
        System.out.println(localDate3);
    }

2.3 解析与格式化

使用java.time.format.DateTimeFormatter对时间和日期进行格式化

@Test
    public void test8() {
        LocalDateTime localDateTime = LocalDateTime.now();
        //第一种对时间日期的格式化方式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yy年MM月dd日 HH:mm:ss");
        String format = formatter.format(localDateTime);
        System.out.println(format);
        //第二种对时间日期的格式化方式
        String format1 = localDateTime.format(DateTimeFormatter.ofPattern("yy年MM月dd日"));
        System.out.println(format1);
    }

2.4 时区的处理

Java8 中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式例如:Asia/Shanghai 等

@Test
    public void test9() {
        //时区
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(ldt);
    }

3 接口中的默认方法与静态方法

3.1 接口中的默认方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用default关键字修饰.

3.1.1 默认方法的使用

public interface MyInterface {
    default String getName(){
        return "呵呵呵";
    }
}

一个实现这个接口的类

public class SubClass implements MyInterface{
}

在主函数中调用

public static void main(String[] args) {
        SubClass sc = new SubClass();
        System.out.println(sc.getName());
    }

打印结果呵呵呵

3.1.2 接口默认方法的”类优先”原则

这时假如这个子类有一个父类,父类中也有一个getName()方法

public class MyClass {
    public String getName(){
        return "嘿嘿嘿";
    }
}

子类此时是

public class SubClass extends MyClass implements MyInterface{
}

这时在主类中掉用

public static void main(String[] args) {
        SubClass sc = new SubClass();
        System.out.println(sc.getName());
    }

结果为嘿嘿嘿

类优先原则:若一个接口中定义了一个默认方法,而另外一个父类中又定义了一个同名的方法时.

选择父类中的方法。如果父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。

3.1.3 接口默认方法的"接口冲突"

"接口冲突":假如子类实现的多个接口中有相同的方法和参数(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

如,子类的另一个实现的接口

public interface MyFun {
    default String getName(){
        return "哈哈哈";
    }
}

这时子类必须覆盖方法,通过接口名来指定到底调用哪一个

public class SubClass implements MyFun,MyInterface{
    @Override
    public String getName() {
        return MyInterface.super.getName();//指定调用的接口
    }
}

在主类中调用

public static void main(String[] args) {
        SubClass sc = new SubClass();
        System.out.println(sc.getName());
    }

执行结果:呵呵呵

3.2 接口中的静态方法

Java8 中,接口中允许添加静态方法。

使用

public interface MyInterface {
    static void show(){
        System.out.println("接口中的静态方法");
    }
}

直接使用类名调用

public static void main(String[] args) {
        MyInterface.show();
    }

输出结果:接口中的静态方法

原文地址:https://www.cnblogs.com/gxl1995/p/cf65f2aefd3f5c3191c36cb69e53bb88.html

时间: 2024-09-30 00:25:02

java8完全解读二的相关文章

SD3.0协议解读二

在阅读本文章之前,请先思考一下什么是总线,总线的作用是什么?相信大家都学过I2C总线,它由SCL和SDA两条线组成,通过这两条线就能完成各种通信.同样地,SD卡通信也需要有自己的总线模式.SD卡还比较牛逼,支持SD总线和SPI总线,老衲接触的比较多的是SD总线,所以这篇文章仅介绍SD总线,对于SPI总线老衲以后有机会再介绍. SD总线: 大家都知道总线一般支持多种频率,在默认的频率下,SD总线支持一(主)对多(从)的模式,即支持一个HOST对多个SD卡的模式.但是,在高速和UHS-I,SD总线只

java8完全解读一

java8完全解读 java8完全解读前言java8的一些新特性1.为什么要用java8?1.1首先想到的逻辑应该是如下1.2使用策略模式来解这个问题1.3使用策略模式和内部类来解决问题1.4使用策略模式和lambda方式来解决这个问题1.5使用stream流来解决这个问题2.lambda基础语法2.1 无参数无返回值2.2有一个参数,并且无返回值2.3 若只有一个参数,小括号可以省略不写2.4 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句函数体需要用{}2.5 若 Lambd

Java8学习笔记(二)-函数式接口与方法引用

一.Lambada表达式到底是什么 首先先看一下下面的一段代码! package cn.org.kingdom.jdk8; @FunctionalInterface interface MyInterface { void test(); String toString(); } public class Test { public void test(MyInterface inter) { System.out.println("*************************"

解读二调

“二调”是什么?      第二次全国土地调查,简称“二调”,作为一项重大的国情国力调查,目的是全面查清目前全国土地利用状况,掌握真实的土地基础数据,建立和完善土地调查.统计和登记制度,实现土地资源信息的社会化服务,满足经济社会发展及国土资源管理的需要. “二调”为什么?       一是国家的需要       土地是重要的资源和资产,在国民经济和社会发展中的地位和作用越来越突出.国家对土地问题十分重视,将土地作为参与宏观调控的重要手段.曾培炎副总理曾明 确指示,土地数据口径要统一,要尽快建立起

NSObject头文件解析 / 消息机制 / Runtime解读 (二)

本章接着NSObject头文件解析 / 消息机制 / Runtime解读(一)写 给类添加属性: BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount) 其中有一个参数我们再在上一篇中提到过 typedef struct { const char *name;           /**< The na

java多线程解读二(内存篇)

线程的内存结构图 一.主内存与工作内存 1.Java内存模型的主要目标是定义程序中各个变量的访问规则.此处的变量与Java编程时所说的变量不一样,指包括了实例字段.静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,因为它们是线程私有的,不会被共享. 2.Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的虚拟内存.线程的虚拟内存中保存了该线程使用到的变量到主内存副本拷贝.线程对变量的所有操作(读取.赋值)都必须在自己的虚拟内存中进行,而不能直接读写主内存中的变量.不同

(转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

转自:http://www.baiyuxiong.com/?p=886 ----------------------------------------------------------------------- 上一篇go语言nsq源码解读-基本介绍  介绍了最基本的nsq环境搭建及使用.在最后使用时,我们用到了几个命令:nsqlookupd.nsqd.nsqadmin.curl及 nsq_to_file,并看到用curl命令写入的几个”hello world”被nsq_to_file命令保

20190825 On Java8 第十二章 集合

第十二章 集合 java.util 库提供了一套相当完整的集合类(collection classes)来解决这个问题,其中基本的类型有 List . Set . Queue 和 Map. 不要在新代码中使用遗留类 Vector ,Hashtable 和 Stack . 泛型和类型安全的集合 new ArrayList<>() 有时被称为"菱形语法"(diamond syntax).在 Java 7 之前,必须要在两端都进行类型声明 ArrayList<Apple&g

20190925 On Java8 第二十二章 枚举

第二十二章 枚举 基本 enum 特性 创建 enum 时,编译器会为你生成一个相关的类,这个类继承自 Java.lang.Enum. valueOf() 是在 Enum 中定义的 static 方法,它根据给定的名字返回相应的 enum 实例,如果不存在给定名字的实例,将会抛出异常. 将静态类型导入用于 enum 使用 static import 能够将 enum 实例的标识符带入当前的命名空间,所以无需再用 enum 类型来修饰 enum 实例. 方法添加 除了不能继承自一个 enum 之外