四、Stream API

1、简介

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

流(Stream) 到底是什么呢?

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

注意:

  • Stream 自己不会存储元素。
  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

2、示例

@Test
public void testStream(){
    //1、创建strean流
    Stream<Person> stream = persons.stream();
    //2、进行中间操作
    stream = stream.filter(p-> p.getAge()>20);
    //3、终止操作
    stream.forEach(System.out::println);

    persons.stream().filter(p->p.getAge()>20).forEach(System.out::println);
}

3、创建strean流

Collection 提供了两个方法 stream() 与 parallelStream()

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

通过 Arrays 中的 stream() 获取一个数组流

Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);

通过 Stream 类中静态方法 of()

Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);

Stream创建无限流

//迭代
Stream<Integer> stream = Stream.iterate(0, (x) -> x + 2);
stream.forEach(System.out::println);

//生成
Stream<Double> stream = Stream.generate(Math::random);
stream.forEach(System.out::println);

4、中间操作

filter—从流中排除某些元素

persons.stream().filter(p->p.getAge()>20).forEach(System.out::println);

imit—截断流,使其元素不超过给定数量

persons.stream().filter(p->p.getAge()>20).limit(2).forEach(System.out::println);

skip(n) —跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流

persons.stream().filter(p->p.getAge()>20).skip(2).forEach(System.out::println);

distinct—筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素

persons.stream().filter(p->p.getAge()>20).distinct().forEach(System.out::println);

映射map , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素

List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");		
Stream<String> stream = strList.stream().map(String::toUpperCase);
stream.forEach(System.out::println);

flatMap—接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");

Stream<Stream<Character>> stream2 = strList.stream()
    .map(StreamTest::filterCharacter);

stream2.forEach(System.out::println);

System.out.println("---------------------------------------------");

stream2.forEach((sm) -> {
    sm.forEach(System.out::println);
});

System.out.println("---------------------------------------------");

Stream<Character> stream3 = strList.stream()
    .flatMap(StreamTest::filterCharacter);

stream3.forEach(System.out::println);

sorted()—自然排序

persons.stream().filter(p->p.getAge()>20).sorted().forEach(System.out::println);

sorted(Comparator com)—定制排序

persons.stream().filter(p->p.getAge()>20).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);

5、终止操作

allMatch—检查是否匹配所有元素

boolean b =persons.stream().filter(p->p.getAge()>20).allMatch(p->p.getAge()==35);

anyMatch—检查是否至少匹配一个元素

boolean b =persons.stream().filter(p->p.getAge()>20).anyMatch(p->p.getAge()==35);

noneMatch—检查是否没有匹配的元素

boolean b =persons.stream().filter(p->p.getAge()>20).noneMatch(p->p.getAge()==35);

findFirst—返回第一个元素

Optional<Person> op = persons.stream().filter(p->p.getAge()>20).findFirst();

findAny—返回当前流中的任意元素

Optional<Person> op = persons.stream().filter(p->p.getAge()>20).findAny();

count—返回流中元素的总个数

long count = persons.stream().filter(p->p.getAge()>20).count();

max—返回流中最大值

persons.stream().map(Person::getSalary).max(Double::compare);

min—返回流中最小值

persons.stream().map(Person::getSalary).min(Double::compare);

归约reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值

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

collect—将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

List<String> liststr  = persons.stream().map(Person::getName).collect(Collectors.toList());

Set<String> setstr  = persons.stream().map(Person::getName).collect(Collectors.toSet());

五、接口中的默认方法与静态方法

JDK8之前,interface中可以定义常量和抽象方法,访问修饰符是public。

public interface A {
    /** a1和a2写法是等价的 */
    public static final int a1 = 0;

    int a2 = 0;

    /** methodA1和methodA2写法是等价的 */
    public abstract void methodA1();

    void methodA2();
}

JDK8起,允许我们在interface中使用static和default修饰方法(使用这两种修饰符中其一就不能使用abstract修饰符),default修饰的方法,通过接口的实现类的对象调用;static修饰的方法,直接通过接口名调用。

public interface MyConstruct<X,Y,Z,T> {

    public T getValue(X x,Y y,Z z);

    default String getValue(Person person){
        return person.toString();
    }

    static Person getObject(){
        return new Person();
    }

六、新时间日期API

1、LocalDate、LocalTime、LocalDateTime

  • LocalDate专门表示日期

  • LocalTime专门表示时间

  • LocalDateTime可以同时表示日期和时间

    // 1.获取当前时间
    LocalDateTime now = LocalDateTime.now();
    System.out.println(now);
    // 2.设置任意时间
    LocalDateTime ldt = LocalDateTime.of(2018,12,8,12,11,13);
    System.out.println(ldt);
    // 3.增加或减少年月日,时分秒
    LocalDateTime ldt2 = ldt.plusYears(2);
    System.out.println(ldt2);
    ldt.minusMonths(1);
    // 4.获取年月日,时分秒
    System.out.println(ldt.getYear());
    System.out.println(ldt.getMonth());
    System.out.println(ldt.getDayOfMonth());
    System.out.println(ldt.getHour());
    System.out.println(ldt.getMinute());
    System.out.println(ldt.getSecond());
    // 5.获取毫秒见
    System.out.println(ldt.getNano());
    System.out.println(ldt.getDayOfWeek());
    System.out.println(ldt.getDayOfYear());

2、Instant : 时间戳。

使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值

@Test
public void testInstant(){
    Instant ins = Instant.now();  //默认使用 UTC 时区
    System.out.println(ins);
    //北京时间(东八区)+8个小时
    OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
    System.out.println(odt);
    System.out.println(ins.getNano());
}

3、Duration Period

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

@Test
public void testInterval(){
    Instant ins1 = Instant.now();

    Instant ins2 = Instant.now();
    ins2 = ins2.plusSeconds(1000);

    System.out.println("时间间隔为:"+Duration.between(ins1,ins2).toMillis());

    LocalDate localDate = LocalDate.now();
    LocalDate localDate1 = LocalDate.of(2019,1,20);
    Period period  = Period.between(localDate,localDate1);
    System.out.println(period.getYears());
    System.out.println(period.getMonths());
    System.out.println(period.getDays());
}

4、TemporalAdjuster 时间校正器

  • dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天

  • firstDayOfMonth 创建一个新的日期,它的值为当月的第一天

  • firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天

  • firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天

  • firstDayOfYear 创建一个新的日期,它的值为当年的第一天

  • firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值

  • lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天

  • lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天

  • lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天

  • lastDayOfYear 创建一个新的日期,它的值为今年的最后一天

  • lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值

  • next/previous 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期

  • nextOrSame/previousOrSame 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象

    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);

    LocalDateTime ldt2 = ldt.withDayOfMonth(10);
    System.out.println(ldt2);

    LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    System.out.println(ldt3);

5、DateTimeFormatter : 解析和格式化日期或时间

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
		
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);

System.out.println(strDate);

LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);

6、ZonedDate、ZonedTime、ZonedDateTime带时区的时间或日期

LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);

七、其他新特性

1、Optional 容器类:用于尽量避免空指针异常

  • Optional.of(T t) : 创建一个 Optional 实例

  • Optional.empty() : 创建一个空的 Optional 实例

  • Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例

  • isPresent() : 判断是否包含值

  • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t

  • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值

  • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()

  • flatMap(Function mapper):与 map 类似,要求返回值必须是Optional

    @Test
    public void testOf(){
    Optional op = Optional.of(new Person(“vesus1”,18,3333));
    System.out.println(op.get());
    }

    @Test
    public void testEmpty(){
    Optional op = Optional.empty();
    System.out.println(op.get());
    }

    @Test
    public void testNullable(){
    Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
    System.out.println(op.get());
    }

    @Test
    public void testPresent(){
    Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
    System.out.println(op.isPresent());

      Optional<Person> op1 = Optional.ofNullable(null);
      System.out.println(op1.isPresent());
    

    }

    @Test
    public void testOrElse(){
    // Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
    Optional op = Optional.ofNullable(null);
    Person person = op.orElse(new Person(“vesus2”,18,5555));
    System.out.println(person);

    }

    @Test
    public void testOrElseGet(){
    // Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
    Optional op = Optional.ofNullable(null);
    Person person = op.orElseGet(Person::new);
    System.out.println(person);
    }

    @Test
    public void testMap(){
    Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
    Optional map = op.map(§->{
    p.setName(“map”);
    return p ;
    });
    System.out.println(map);
    }

2、重复注解

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnos {
    MyAnno[] value();
}

@Repeatable(MyAnnos.class)
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
    String value();
}

public class AnnoTest {

    @MyAnno("vesus")
    @MyAnno("vesus11")
    public void test(){
        System.out.println("hello word");
    }
}

3、jdk8中对jvm优化

在这里插入图片描述

1、虚拟机栈:每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。栈的大小可以固定也可以动态扩展。当栈调用深度大于JVM所允许的范围,会抛出StackOverflowError的错误

2、本地方法栈:这部分主要与虚拟机用到的 Native 方法相关,一般情况下, Java 应用程序员并不需要关心这部分的内容。线程私有。

3、PC 寄存器:PC 寄存器,也叫程序计数器。JVM支持多个线程同时运行,每个线程都有自己的程序计数器。倘若当前执行的是 JVM 的方法,则该寄存器中保存当前执行指令的地址;倘若执行的是native 方法,则PC寄存器中为空。

4、堆:堆内存是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过 GC 进行回收。当申请不到空间时会抛出 OutOfMemoryError。

5、方法区:方法区也是所有线程共享。主要用于存储类信息、常量池、静态变量、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。

在这里插入图片描述

所有新创建的Object 都将会存储在新生代Yong Generation中。如果Young Generation的数据在一次或多次GC后存活下来,那么将被转移到OldGeneration。

永久代存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息

jdk8中引入了元空间的概念

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

在这里插入图片描述

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐