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

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();
    }

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

java8完全解读二的更多相关文章

  1. java8完全解读一

    java8完全解读 java8完全解读前言java8的一些新特性1.为什么要用java8?1.1首先想到的逻辑应该是如下1.2使用策略模式来解这个问题1.3使用策略模式和内部类来解决问题1.4使用策略 ...

  2. jQuery.Callbacks 源码解读二

    一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...

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

    线程的内存结构图 一.主内存与工作内存 1.Java内存模型的主要目标是定义程序中各个变量的访问规则.此处的变量与Java编程时所说的变量不一样,指包括了实例字段.静态字段和构成数组对象的元素,但是不 ...

  4. mybatis源码解读(二)——构建Configuration对象

    Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...

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

    转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...

  6. cJONS序列化工具解读二(数据解析)

    cJSON数据解析 关于数据解析部分,其实这个解析就是个自动机,通过递归或者解析栈进行实现数据的解析 /* Utility to jump whitespace and cr/lf *///用于跳过a ...

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

    本章接着NSObject头文件解析 / 消息机制 / Runtime解读(一)写 给类添加属性: BOOL class_addProperty(Class cls, const char *name, ...

  8. Nginx 小入门记录 之 Nginx 配置文件解读(二)

    上一小节主要是记录一些环境准备和Nginx的安装,接下来对Nginx基本配置进行记录. 查看配置文件安装记录 可以通过以下Linux命令进行查看: rpm -ql nginx rpm 是liunx的包 ...

  9. 20190925 On Java8 第二十二章 枚举

    第二十二章 枚举 基本 enum 特性 创建 enum 时,编译器会为你生成一个相关的类,这个类继承自 Java.lang.Enum. valueOf() 是在 Enum 中定义的 static 方法 ...

随机推荐

  1. JAVA DOM4j解析XML数据到自定义javabean

    我们获取xml中的数据,一般以面向对象的思想去处理这些数据.因此,我们需要自定义类来封装解析出来的数据,以方便我们操作这些数据. 自定义的java类,称为javabean. 自定义Contact类代码 ...

  2. 【leetcode81】Product of Array Except Self

    题目描述: 给定一个长度为n的整数数组Array[],输出一个等长的数组result[],这个输出数组,对应位置i是除了Array[i]之外,其他的所有元素的乘积 例如: given [1,2,3,4 ...

  3. 【一天一道LeetCode】#63. Unique Paths II

    一天一道LeetCode (一)题目 Follow up for "Unique Paths": Now consider if some obstacles are added ...

  4. Linux 学习笔记_12_文件共享服务_2_FTP应用--vsftpd

    Wu-FTP:古老,配置复杂 Proftp:功能强大 vsftp: 安全,高速,稳定[系统默认的FTP软件] VSFTP服务器配置 启动:/etc/rc.d/init.d/vsftpd start [ ...

  5. 017-封装-OC笔记

    学习目标 1.[了解]异常处理 2.[掌握]类方法 3.[掌握]NSString类 4.[掌握]匿名对象 5.[掌握]封装实例变量 6.[掌握]对象之间的关系 一.异常处理 什么是异常? 代码完全符合 ...

  6. (二十五)键盘的设置与TextField细节处理

    设置Return Key类型为Send并且勾选下面的复选框即可实现没有内容时禁用keyboard,有内容自动启用. 文本框左边框与文本留有间距的方法: //文本框左侧留下间距 UIView *left ...

  7. 对C语言中递归算法的分析

    C通过运行时堆栈支持递归函数的实现.递归函数就是直接或间接调用自身的函数.     许多教科书都把计算机阶乘和菲波那契数列用来说明递归,非常不幸我们可爱的著名的老潭老师的<C语言程序设计> ...

  8. 【Qt编程】Qt学习笔记<二>

    1.      QApplication类和QWidget类都包含在QtGui模块中.所以我们可以只包含这个头文件即可. 2.      在c++中,用new分配了内存空间就需要用delete来释放空 ...

  9. 【翻译】在Ext JS应用程序中使用自定义图标

    原文:Using Custom Icons in Your Ext JS App 作者:Lee BoonstraLee is a technical trainer at Sencha. She's ...

  10. Rest api简介

    理解和使用内容协商 我们的开发者在发送一个 REST API 请求的同时,根据应用场景,针对相同的资源,可能会期待不同的返回形式. 比如,我希望根据用户客户端语言,同一个资源的内容可以返回不同的语言. ...