继续着上次的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. C语言--字符串和数字的相互转换

    1.数字转换为字符串 sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出. sprintf 是个变参函数,定义如下: int sp ...

  2. Chipmunk僵尸物理对象的出现和解决(八)

    如何解决? 等到碰撞方法返回后在调用Star类方法.碰撞方法在物理引擎的一帧内应该会处理完成,在下一帧里碰撞回调已经结束.所以我们将Star类方法的调用放到下一帧里执行即可,代码如下: //... @ ...

  3. iOS-导航头像缩放,支持点击回调

    在很多App中,经常存在一种需求就是,界面上下滚动时用户的头像也会跟着滚动,而用户头像在视图向上滚动一定范围时停留并在导航栏的位置 基本用法如下:1.单纯的实现这一效果: - (LEOHeaderVi ...

  4. JAVA之旅(十二)——Thread,run和start的特点,线程运行状态,获取线程对象和名称,多线程实例演示,使用Runnable接口

    JAVA之旅(十二)--Thread,run和start的特点,线程运行状态,获取线程对象和名称,多线程实例演示,使用Runnable接口 开始挑战一些难度了,线程和I/O方面的操作了,继续坚持 一. ...

  5. 【一天一道LeetCode】#71. Simplify Path

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  6. [Ext.Net]TreePanel+gridPanel实例

     @小花要完整例子,尝试一下图文并茂,力求完整. gridPanel TreePanel.JPG (27.49 KB, 下载次数: 16) 下载附件  保存到相册 2013-1-6 11:24 上 ...

  7. (四十三)UITabBarController和AppDelegate的一些细节

    假设一个UITabBar是V1,V2,V3三个视图的根控制器. 一进入程序的时候,只会加载一个视图V1,也就是说UITabBar是延迟加载的. Tip:通过代码创建的UITabBar会一次性加载所有视 ...

  8. Python的time(时间戳与时间字符串互相转化)

    strptime("string format")字符串如"20130512000000"格式的 输入处理函数 localtime(float a)时间戳的输入 ...

  9. 在多台PC上进行ROS通讯-学习笔记

    首先,致谢易科(ExBot)和ROSWiki中文社区. 重要参考文献: Running ROS across multiple machines http://wiki.ros.org/ROS/Tut ...

  10. 总结C语言在嵌入式开发中应用的知识点(文件数据的加密与解密)

    <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255) ...