一文搞定java8中的重要特性

Lambda表达式

看下以下四种写法:

package lambda;

import java.util.Arrays;

/**
 * @ClassName lambdaDemo
 * @Deacription TODO
 * @Author linsen
 * @Date 2020/11/16 19:21
 * @Version 1.0
 **/
public class lambdaDemo {
    public static void main(String[] args) {
        for (String e : Arrays.asList("a", "b", "c")) {
            System.out.println(e);
        }
        Arrays.asList("a", "b", "c").forEach(e -> System.out.println(e));
        Arrays.asList("a", "b", "c").forEach((String e) -> System.out.println(e));
        Arrays.asList("a", "b", "c").forEach((String e) -> {
            System.out.println(e);
        });
    }
}

这几种写法都是遍历新生成的包含a,b,c三个元素的集合。第一种是java8之前的写法,而后三种都是java8的写法,称为Lambda表达式。

Lambda表达式是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。在java8之前,一直使用匿名内部类代替Lambda表达式。

Lambda中可以引用类成员和局部变量,这些变量会被隐式的转化为final。

package lambda;

import java.util.Arrays;

/**
 * @ClassName lambdaDemo1
 * @Deacription TODO
 * @Author linsen
 * @Date 2020/11/16 19:31
 * @Version 1.0
 **/
public class lambdaDemo1 {
    public static void main(String[] args) {
        String s = ",";
        Arrays.asList( "a", "b", "d" ).forEach(
                ( String e ) -> System.out.print( e + s ) );
    }
}

Lambda表达式有返回值,返回值的类型也由编译器推理得出。如果Lambda表达式中的语句块只有一行,则可以不用使用return语句,下列两个代码片段效果相同:

Arrays.asList( "a", "b", "d" ).sort((e1,e2)->e1.compareTo(e2));
        Arrays.asList( "a", "b", "d" ).sort((e1,e2)->{
            int num = e1.compareTo(e2);
            return num;
        });

函数式接口

Lambda的设计者们为了让现有的功能与Lambda表达式良好兼容,考虑了很多方法,于是产生了函数接口这个概念。函数接口指的是只有一个函数的接口,这样的接口可以隐式转换为Lambda表达式。

在实践中,函数式接口非常脆弱:只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解@FunctionalInterface。

@FunctionalInterface
public interface Functional {
    void method();
}

不过有一点需要注意,默认方法和静态方法不会破坏函数式接口的定义,因此如下的代码是合法的。

@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();
        
    default void defaultMethod() {            
    }        
}

四大内置函数接口

Consumer(消费型接口)接收一个参数,无返回值:


@FunctionalInterface
public interface Consumer<T> {
 
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}

Supplier(提供型接口) 无参,有返回值

@FunctionalInterface
public interface Supplier<T> {
 
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
 }

Function(函数接口)接收一个参数,有返回值

@FunctionalInterface
public interface Function<T, R> {
 
    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
};

Predicat(断言型接口)接收一个参数,返回boolean:

@FunctionalInterface
public interface Predicate<T> {
 
    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

接口的默认方法和静态方法

在java8之前,接口中定义的方法都需要被它的实现类去实现,而在java8中,出现了默认方法和静态方法。

默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写,也就是说可以实现也可以不实现:

private interface Defaulable {
    default String notRequired() { 
        return "Default implementation"; 
    }        
}        
private static class DefaultableImpl implements Defaulable {
}
private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}

这两个实现类,一个覆盖了默认方法,一个继承了默认方法,都是没问题的。

java8的接口中也可以定义静态方法。

public interface Demoface {
    static void say(){
        System.out.println("Demoface");
    }
}

可以直接调用接口中的静态方法。

public class MainDemo {
    public static void main(String[] args) {
        Demoface.say();
    }
}

方法引用

方法引用使得开发者可以直接引用现存的方法、Java类的构造方法或者实例对象。方法引用和Lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。

引用方法有下面几种方式:
(1)对象引用::实例方法名
(2)类名::静态方法名
(3)类名::实例方法名
(4)类名::new

对象引用::实例方法名:

public class Car {
    private long price;
    public Car() {
        
    }
    public Car(long price) {
        this.price = price;
    }
    public long getPrice() {
        return price;
    }
    public void setPrice(long price) {
        this.price = price;
    }
}
public class CarCpmapre {
    public int priceCompare(Car a, Car b) {
        return (int) (a.getPrice() - b.getPrice());
    }

    public static void main(String[] args) {
        Car[] cars = new Car[]{
                new Car(100),
                new Car(300),
                new Car(200),
        };
        CarCpmapre carCpmapre = new CarCpmapre();
        Arrays.sort(cars,carCpmapre::priceCompare);
        for (Car car : cars) {
            System.out.println(car.getPrice());
        }
    }
}

类名::静态方法名:
举几个例子:
String::valueOf,等价于 Lambda:s -> String.valueOf(s);
Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y);
可以直接使用类的静态方法。

类名::实例方法名:
若Lambda表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法:

public class MainDemo {
    public static void main(String[] args) {
        BiPredicate<String,String> biPredicate = String::contains;
        System.out.println(biPredicate.test("1213","121"));
    }
}

类名::new:
在引用构造器的时候,构造器参数列表要与接口中抽象方法的参数列表一致。

public class CarFactory {
    private Supplier<Car> supplier;
    public CarFactory(Supplier<Car> supplier) {
        this.supplier = supplier;
    }
    public Car getCar(){
        return supplier.get();
    }
    public static void main(String[] args) {
        CarFactory carFactory = new CarFactory(Car::new);
        carFactory.getCar();
    }
}

这里的Car类,还是上文中的Car类。

重复注解

在 Java 8 之前我们不能在一个类型重复使用同一个注解
比如:

@PropertySource("classpath:config.properties")
@PropertySource("file:application.properties")
public class MainApp {}

在java7中是报错的。在java7中,我们只有通过嵌套的方式来解决达到这个效果:

@Retention(RetentionPolicy.RUNTIME)
public @interface PropertySources {
  PropertySource[] value();
}

使用时:

@PropertySources({
  @PropertySource("classpath:config.properties"),
  @PropertySource("file:application.properties")
})
public class MainApp {
}

但是在java8中,引入了重复注解的概念。

@Retention(RetentionPolicy.RUNTIME)
@Repeatable(PropertySources.class) //这行建立了 @PropertySource 与 @PropertySources 的关系
public @interface PropertySource {
  String value();
}

只需要声明@Repeatable(PropertySources.class) ,就可以使用重复注解。这里这个申明,其实和上面的java7中的嵌套注解的写法意义是相同的,一个是显式嵌套,一个是隐式嵌套。使用代码:

@PropertySource("classpath:config.properties")
@PropertySource("file:application.properties")
public class MainApp {}

所以java7,java8中都可以使用一个代码去获取注解值:

PropertySources annotation = MainApp.class.getAnnotation(PropertySources.class);
for (PropertySource propertySource: annotation.value()){
  System.out.println(propertySource.value());
}

不过在java8中,有更推荐的获取重复注解的方式:

PropertySource[] propertySources = MainApp.class.getAnnotationsByType(PropertySource.class);
for (PropertySource propertySource: propertySources ){
  System.out.println(propertySource.value()); //获得所有 @PropertySource 的内容
}

更好的类型推断

Java 8编译器在类型推断方面有很大的提升,在很多场景下编译器可以推导出某个参数的数据类型,从而使得代码更为简洁。

拓宽注解的应用场景

Java 8拓宽了注解的应用场景。现在,注解几乎可以使用在任何元素上:局部变量、接口类型、超类和接口实现类,甚至可以用在函数的异常定义上。

Optional

NullPointerException相信每个JAVA程序员都不陌生,是JAVA应用程序中最常见的异常。JDK8的类中引入了Optional类,来解决这个问题。
Optional是一个容器对象,它可能包含空值,也可能包含非空值。当属性value被设置时,isPesent()方法将返回true,并且get()方法将返回这个值。

我们可以用Optional.ofNullable()或者Optional.of()的方式生成一个Optional对象,前者可以传入null值,后者只能传入非空对象。

  Optional<String> optionalS1 = Optional.ofNullable(null);
  Optional<String> optionalS2 = Optional.ofNullable("null");
  Optional<String> optionalS3 = Optional.of("null");

我们可以调用isPresent()和get()方法来判断Optional中的对象是否为null和在非null的情况下获得该对象。

String s = "123";
        Optional<String> optionalS = Optional.of(s);
        if (optionalS.isPresent()){
            System.out.println(optionalS.get());
        }

我们可以在Optional为空的时候使用orElseGet传入一个提供方法,返回一个新的值去取代null。

		String s = null;
		Optional<String> optionalS = Optional.ofNullable(s);
		System.out.println(optionalS.orElseGet(() -> "123"));

也可以直接用orElse传入自己需要的对象。

        String s = null;
        Optional<String> optionalS = Optional.ofNullable(s);
        System.out.println(optionalS.orElse("123"));

用ifPresent去消费。

        String s = "123";
        Optional<String> optionalS = Optional.ofNullable(s);
        optionalS.ifPresent(System.out::println);

调用map方法,传入一个函数式接口,可以得到经过函数处理的Optional。

//返回1232
        String s = "123";
        Optional<String> optionalS = Optional.ofNullable(s);
        System.out.println(optionalS.map(s1->s1+"2").get());

用flatmap方法,和上面一个效果。

public class optionalDemo {
    public static void main(String[] args) {
        String s = "123";
        Optional<String> optionalS = Optional.ofNullable(s);
        System.out.println(optionalS.flatMap(optionalDemo::apply).get());
    }
    private static Optional<String> apply(String s1) {
        return Optional.of(s1 + 2);
    }
}

可以调用filter传入断言去过滤。

        String s = "123";
        Optional<String> optionalS = Optional.ofNullable(s);
        System.out.println(optionalS.filter(s1 -> s1.equals("1234")));

Stream

新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中。这是目前为止最大的一次对Java库的完善,以便开发者能够写出更加有效、更加简洁和紧凑的代码。

下面来看它的使用实例,先给出一个实体类:

public class Good {
    private String name;               // 商品名称
    private long price;               // 价格
    private long sales;               // 销量
    private List<String> categories;  // 类别
    //省略构造器,getset,toString等
}

现在需要将商品中价格前十名且价格大于200的商品设为半价,如果在java7中,这可能很麻烦,但是使用Stream来操作,我们只需要以下方法即可:

void pross(List<Good> goods){
        goods.stream().filter(good -> good.getPrice()>200)
                .sorted((g1,g2)-> (int) (g2.getPrice()-g1.getPrice()))
                .limit(10).forEach(good -> good.setPrice(good.getPrice()/2));
    }

goods集合提供了元素序列的数据源,通过 stream() 方法获得 Stream,filter / sorted / limit 进行数据处理,“连接起来” 构成 “流水线”,forEach 最终执行。需要说明,filter / sorted / limit 的返回值均为 Stream,但它们并不立即执行,而是构成了 “流水线”,直到 forEach:最终执行,并且关闭 Stream。
像filter / sorted / limit这种构成流水线的,我们称为中间操作,而像forEach,则称为终止操作。

下面展示一下Stream的一些常用操作,还是上面的商品集合goods。

生成Stream。

goods.stream().forEach(System.out::println);

过滤操作。

goods.stream().filter(good -> good.getPrice()>200).forEach(System.out::println);

anyMatch / allMatch / noneMatch都是终止操作,传入断言条件,分别判断集合中是否含有符合条件的元素,是否全是符合条件的元素,是否都不符合条件,返回值为boolen类型。

boolean flag = goods.stream().anyMatch(good -> "name".equals(good.getName()));

findAny、findFirst,都是 “终止操作”,分别获取 Stream 元素序列的任意元素和第一个元素。

        Optional<Good> optionalGood = goods.stream().filter(c -> "name".equals(c.getName())).findAny();
        Optional<Good> optionalGood1 = goods.stream().filter(c -> "name".equals(c.getName())).findFirst();

map 是中间操作,将 Stream 序列的元素映射为其他的元素。

goods.stream().map(good -> good.getName()).forEach(System.out::println);

map 直接将 Stream 序列的元素映射到新的元素,假如 map 映射获得的是 Stream,flatMap 能够将各个 Stream 的元素合并到一个 Stream 中。其实可以理解成和map一样,只不过方法实现里要返回Stream。

goods.stream().flatMap(good -> Stream.of(good.getName())).forEach(System.out::println);

distinct 是 “中间操作”,即去重,去重的依据即为 Stream 序列元素类型的 equals 和 hashCode 方法。

goods.stream().map(Good::getName).distinct().forEach(System.out::println);

sorted用于排序,底下两行是一个效果。

goods.stream().sorted((g1,g2)-> (int) (g2.getPrice()-g1.getPrice())).forEach(System.out::println);
goods.stream().sorted(Comparator.comparing(Good::getPrice).reversed()).forEach(System.out::println);

limit / skip 是 “中间操作”,接收 long 类型的参数,实现 Stream 序列元素的截取和跳过。

//取第三个开始后的五个
goods.stream().skip(2).limit(5).forEach(System.out::println);

count / min / max是终止操作,分别返回元素数量,最小元素和最大元素。

goods.stream().count();
goods.stream().max(Comparator.comparing(Good::getPrice).reversed());
goods.stream().min(Comparator.comparing(Good::getPrice).reversed());

reduce,归约,是终止操作,用于将 Stream 中的元素归约到一个具体的值。
看下底下二个实例,是得到商品的价格总额。

        goods.stream().reduce((g1,g2)->{
            Good good = new Good();
            good.setPrice(g1.getPrice()+g2.getPrice());
            return good;
        }).get();
        //传入apply方法参与聚合,结果为空时返回第一个参数,不为空时第一个参数也参与运算
        goods.stream().reduce(new Good(),(g1,g2)->{
            Good good = new Good();
            good.setPrice(g1.getPrice()+g2.getPrice());
            return good;
        }).getPrice();

着重第三种reduce的方法。

<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

后面两个参数其实就是两个apply函数,值得一提的是,最后一个参数只有在并行Stream中才会有用,在单线程下是无用的。
他的执行流程可以理解为,在每个线程中将第一个参数在第二个参数的方法中计算出结果,最后再用第三个方法去把这些线程的结果合并计算。
给一个示例:

       int s =  Arrays.asList(2,3,4,5,6).stream().parallel().reduce(3,(a,b)->a+b,(a,b)->{
           System.out.println("id"+Thread.currentThread().getId()+"\t"+a+"+"+b);
           return a+b;
       });

打印结果是:
在这里插入图片描述
结合上面的说法,思考一下,流程应该很明确了。

collect,是终止操作,collect 即 收集数据。collect以Collector<? super T, A, R> 作为参数,通常,我们使用 Collectors 提供的辅助函数获得收集器实例。看以下方法。
返回一个List示例。

goods.stream().map(Good::getName).collect(Collectors.toList());

返回一个Set示例。

goods.stream().map(Good::getName).collect(Collectors.toSet());

返回一个Map示例,按价格分组。

Map m = goods.stream().collect(Collectors.groupingBy(Good::getPrice));

与groupingBy类似,返回一个Map,但是按照是否符合条件按true,false分组。

Map m1 = goods.stream().collect(Collectors.partitioningBy(c -> c.getSales() >= 200));

reducing,Collectors.reducing 参数与 Stream.reduce 一致,作为 collect 参数,能够与 reduce 获得相同的结果。

通过 “并行 Stream” 即可获得 Stream API 的并行能力。

        goods.stream().parallel();
        goods.parallelStream();

并行 Stream 具有并行能力,其默认线程数量即为通过 Runtime.getRuntime().availableProcessors() 获得的线程数,并行未必能够提高性能,通常适用于 Stream 元素数量大、或单个元素处理非常耗时的场景。

Date/Time API

java8推出了更好的时间相关的API。

LocalDate:
看下localDate的创建。

        //创建一个当前日期的LocalDate
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate);
        //指定年月日创建LocalDate
        localDate=LocalDate.of(2020,11,11);
        System.out.println(localDate);
        //指定某年的第多少天创建
        localDate = LocalDate.ofYearDay(2020, 256);
        System.out.println(localDate);

得到LocalDate的信息。

        System.out.println(localDate.getYear());
        System.out.println(localDate.getDayOfMonth());
        System.out.println(localDate.getDayOfYear());
        System.out.println(localDate.getMonthValue());

使用 TemporalField 读取 LocalDate 的值,ChronoField 是个枚举,实现了TemporalField接口,我们可以使用ChronoField方式读取date。

		LocalDate date = LocalDate.of(2019, 10, 27);
        // 获得年份 2019
        System.out.println(date.get(ChronoField.YEAR));
        // 获得月份 10
        System.out.println(date.get(ChronoField.MONTH_OF_YEAR));
        // 获得这个月中的第几天 27
        System.out.println(date.get(ChronoField.DAY_OF_MONTH));
        // 获得这个星期的第几天 7
        System.out.println(date.get(ChronoField.DAY_OF_WEEK));

将String解析为LocalDate。

        String dateStr = "2019-10-27";
        LocalDate parse = LocalDate.parse(dateStr);
        System.out.println(parse);//2019-10-27
        // 指定格式解析
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        LocalDate date = LocalDate.parse("2019/10/27", dateTimeFormatter);
        System.out.println(date);//2019-10-27

使用 Period 操纵 date,获得连个日期之间的差值。

        LocalDate date1 = LocalDate.of(2019, 10, 27);
        LocalDate date2 = LocalDate.of(2019, 12, 25);
        Period between = Period.between(date1, date2);
        System.out.println(between.getDays());// 28
        System.out.println(between.getMonths());//1

通过withAttribute修改不会改变原来的date,会在原来date的基础上形成新的LocalDate副本。

		LocalDate date1 = LocalDate.of(2019, 10, 27);
        LocalDate date2 = date1.withMonth(9);//2019-09-27
        System.out.println(date2);
        LocalDate date3 = date2.withYear(2018);//2018-09-27
        System.out.println(date3);
        // 2019-10-27
        System.out.println(date1);

TemporalAdjuster 时间矫正器修改时间也是不会改变原来的date,会新生成LocalDate 副本,相比于withAttribute,其API更加丰富,提供大量的静态工厂方法。

        LocalDate date1 = LocalDate.of(2019, 10, 27);
        LocalDate date2 = date1.with(TemporalAdjusters.firstDayOfMonth());// 2019-10-01
        LocalDate date3 = date1.with(TemporalAdjusters.firstDayOfYear());
        // 2019-01-01
        System.out.println(date3);
        LocalDate date4 = date1.with(TemporalAdjusters.lastDayOfYear());
        // 2019-12-31
        System.out.println(date4);

LocalTime:
LocalTime的用法和LocalDate很相似。

//创建时间
        LocalTime now = LocalTime.now();
        System.out.println(now);//22:49:03.360
        LocalTime of = LocalTime.of(22, 47);
        System.out.println(of);//22:47
//修改时间
		LocalTime time = LocalTime.of(22, 50);
        LocalTime time1 = time.withHour(2);//02:50
        System.out.println(time1);
        LocalTime time2 = time.withMinute(10);//22:10
        System.out.println(time2);
//获得时间秒差        
        LocalTime time1 = LocalTime.of(22, 50);
        LocalTime time2 = LocalTime.of(23, 10);
        Duration duration = Duration.between(time1,time2);
        System.out.println(duration.getSeconds());

LocalDateTime:
LocalDate 和 LocalTime 能相互合并成 LocalDateTime ,LocalDateTime 也可以转为 LocalDate 或者 LocalTime。其他用法和之前也很相似。

        LocalDate localDate = LocalDate.of(2019, 10, 27);
        LocalTime localTime = LocalTime.of(23, 20, 00);
        // 合并为 LocalDateTime
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        //指定格式输出
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd hh:mm:ss");
        String ldt = dateTimeFormatter.format(localDateTime);
        System.out.println(ldt);//2019-10-27T23:20
        // 转为LocalDate
        LocalDate localDate1 = localDateTime.toLocalDate();
        System.out.println(localDate);//2019-10-27
        // 转为 LocalTime
        LocalTime localTime1 = localDateTime.toLocalTime();
        System.out.println(localTime);// 23:20

Instant :
计算机存储的当前时间,本质上只是一个不断递增的整数。Java提供的System.currentTimeMillis()返回的就是以毫秒表示的当前时间戳。
这个当前时间戳在java.time中以Instant类型表示,我们用Instant.now()获取当前时间戳,效果和System.currentTimeMillis()类似。

        Instant now = Instant.now();
        System.out.println(now.getEpochSecond()); // 秒
        System.out.println(now.toEpochMilli()); // 毫秒 

ZoneId:
java8中 java.time.ZoneId代替了老版本java.util.TimeZone 。
默认是当前时区和UTC /格林威治的固定偏差值。

        ZoneId shanghai = ZoneId.of("Asia/Shanghai");
        LocalDate date = LocalDate.of(2019, 10, 27);
        // 设置时区
        ZonedDateTime zonedDateTime = date.atStartOfDay(shanghai);
        // 获得偏移
        ZoneOffset offset = zonedDateTime.getOffset();
        System.out.println(offset);//+08:00

时区时间计算。

        // 芝加哥时间
        ZoneId zoneId = ZoneId.of("America/Chicago");
        Instant instant = Instant.now();
        ZonedDateTime zonedDateTime = instant.atZone(zoneId);
        LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
        System.out.println(localDateTime);
        //上海时间
        ZoneId zoneId1 = ZoneId.of("Asia/Shanghai");
        LocalDateTime localDateTime1 = LocalDateTime.ofInstant(instant,zoneId1);
        System.out.println(localDateTime1);

Base64

对Base64编码的支持已经被加入到Java 8官方库中,这样不需要使用第三方库就可以进行Base64编码,例子代码如下:

import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Base64s {
    public static void main(String[] args) {
        final String text = "Base64 finally in Java 8!";
        
        final String encoded = Base64
            .getEncoder()
            .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
        System.out.println( encoded );
        
        final String decoded = new String( 
            Base64.getDecoder().decode( encoded ),
            StandardCharsets.UTF_8 );
        System.out.println( decoded );
    }
}

并行数组

Java8除Stream并行流外,对于普通数组,Java8提供了也简单的并行功能。
例如数组排序,一般使用Arrays.sort()方法串行排序,Java8新增方法Arrays.parallelSort()并行排序。
先用parallelSetAll并行随机插入数组元素,之后并行排序:

        Long[] longs = new Long[1000];
        Arrays.parallelSetAll(longs, index ->
                ThreadLocalRandom.current().nextLong(1000000));
        Arrays.parallelSort(longs);
        Arrays.stream(longs).limit(10).forEach(System.out::println);

数组也可以用Stream来操作,

		Long[] longs = new Long[1000];
        Arrays.parallelSetAll(longs, index ->
                ThreadLocalRandom.current().nextLong(10000));
        Arrays.parallelSort(longs);
        Map<Boolean, List<Long>> hashMap = Arrays.stream(longs).parallel().collect(Collectors.partitioningBy(l->{
            return l>2000;
        }));
        hashMap.get(true).stream().limit(10).forEach(System.out::println);

并发性

以下是官方文档的原话翻译。

在新增Stream机制与lambda的基础之上,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持聚集操作。同时也在java.util.concurrent.ForkJoinPool类中加入了一些新方法来支持共有资源池(common pool)(请查看我们关于Java 并发的免费课程)。

新增的java.util.concurrent.locks.StampedLock类提供一直基于容量的锁,这种锁有三个模型来控制读写操作(它被认为是不太有名的java.util.concurrent.locks.ReadWriteLock类的替代者)。

在java.util.concurrent.atomic包中还增加了下面这些类:

DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder

JVM的新特性

使用Metaspace(元空间)代替持久代。
在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值