一文搞定java8中的重要特性

2023-11-16

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。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文搞定java8中的重要特性 的相关文章

  • Grails 3.x bootRun 失败

    我正在尝试在 grails 3 1 11 中运行一个项目 但出现错误 失败 构建失败并出现异常 什么地方出了错 任务 bootRun 执行失败 进程 命令 C Program Files Java jdk1 8 0 111 bin java
  • 如何为最终用户方便地启动Java GUI程序

    用户想要从以下位置启动 Java GUI 应用程序Windows 以及一些额外的 JVM 参数 例如 javaw Djava util logging config file logging properties jar MyGUI jar
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 加速代码 - 3D 数组

    我正在尝试提高我编写的一些代码的速度 我想知道从 3d 整数数组访问数据的效率如何 我有一个数组 int cube new int 10 10 10 我用价值观填充其中 然后我访问这些值数千次 我想知道 由于理论上所有 3d 数组都存储在内
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 我可以使用 HSQLDB 进行 junit 测试克隆 mySQL 数据库吗

    我正在开发一个 spring webflow 项目 我想我可以使用 HSQLDB 而不是 mysql 进行 junit 测试吗 如何将我的 mysql 数据库克隆到 HSQLDB 如果您使用 spring 3 1 或更高版本 您可以使用 s
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • 禁止的软件包名称:java

    我尝试从数据库名称为 jaane 用户名 Hello 和密码 hello 获取数据 错误 java lang SecurityException Prohibited package name java at java lang Class
  • 如何为俚语和表情符号构建正则表达式 (regex)

    我需要构建一个正则表达式来匹配俚语 即 lol lmao imo 等 和表情符号 即 P 等 我按照以下示例进行操作http www coderanch com t 497238 java java Regular Expression D
  • 如何使用 LAMBDA 表达式在 LINQ 中执行 IN 或 CONTAINS?

    我有以下 Transact Sql 我正在尝试将其转换为 LINQ 并且很挣扎 SELECT FROM Project WHERE Project ProjectId IN SELECT ProjectId FROM ProjectMemb
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 如何在桌面浏览器上使用 webdriver 移动网络

    我正在使用 selenium webdriver 进行 AUT 被测应用程序 的功能测试自动化 AUT 是响应式网络 我几乎完成了桌面浏览器的不同测试用例 现在 相同的测试用例也适用于移动浏览器 因为可以从移动浏览器访问 AUT 由于它是响
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 静态变量的线程安全

    class ABC implements Runnable private static int a private static int b public void run 我有一个如上所述的 Java 类 我有这个类的多个线程 在里面r
  • JGit 检查分支是否已签出

    我正在使用 JGit 开发一个项目 我设法删除了一个分支 但我还想检查该分支是否已签出 我发现了一个变量CheckoutCommand但它是私有的 private boolean isCheckoutIndex return startCo
  • 如何修复 JNLP 应用程序中的“缺少代码库、权限和应用程序名称清单属性”?

    随着最近的 Java 更新 许多人都遇到了缺少 Java Web Start 应用程序的问题Codebase Permissions and Application name体现属性 尽管有资源可以帮助您完成此任务 但我找不到任何资源综合的
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List

随机推荐

  • C#中Linq用法汇集

    C 中Linq常用用法 LINQ查询语法 有两种基本的方法来编写一个LINQ查询IEnumerable集合或IQueryable数据源 1 查询语法或查询表达式语法 2 方法语法或方法扩展语法或Fluent LINQ查询语法 查询语法与数据
  • linux各目录详细介绍

    bin 二进制可执行命令 dev 设备特殊文件 etc 系统管理和配置文件 etc rc d 启动的配置文件和脚本 home 用户主目录的基点 比如用户user的主目录就是 home user 可以用 user表示 lib 标准程序设计库
  • stm32定时器外部计数模式 最大频率计算。

    本 外部时钟模式下 外部信号通过内部时钟 CK INT 采样得到 外部信号周期的计算方法是 2xTINT 20ns 这里的TINT是CK INT的周期时间 例如 内部时钟频率为72MHz 则TINT 13 89ns 这时外部信号的最高频率是
  • flask 第七章 简陋版智能玩具 +MongoDB初识和基本操作

    1 简陋版web智能玩具 FAQ py文件 import os from aip import AipSpeech AipNlp from uuid import uuid4 你的 APPID AK SK APP ID 16027160 A
  • Git命令查看以及修改用户名和密码

    一 查看 查看用户名 git config user name 查看密码 git config user password 查看邮箱 git config user email 查看配置信息 git config list 二 修改 修改用
  • ios删除分割线_iOS开发中隐藏导航栏的分割线

    iOS开发中经常会遇到需要隐藏导航栏的分割线的需求 之前已经有两种方法 方法一 直接设置navigationBar的ShadowImage和BackgroundImag 这样做可以一劳永逸 在当前导航控制器的所有子控制器页面看到的导航栏都是
  • js判断时间大小以及计算时间差

    有些时候我们需要使用JS判断一下不同格式的时间的大小该怎判断呢 例如 2019 06 26 14 00 00 这个格式的时间 如何判断是否大于现在的时间 function compareTimeNow time var strArray t
  • osgEarth的Rex引擎原理分析(十五)分页瓦片加载器在更新遍历时对请求处理过程

    目标 十四 中的34 osgEarthDrivers engine rex Loader cpp void PagerLoader traverse osg NodeVisitor nv for count 0 count lt merge
  • Java-基于SSM的智能仓储管理系统

    项目背景 随着科学技术的飞速发展 社会的方方面面 各行各业都在努力与现代的先进技术接轨 通过科技手段来提高自身的优势 智能仓储系统当然也不能排除在外 智能仓储系统是以实际运用为开发背景 运用软件工程开发方法 采用jsp技术构建的一个管理系统
  • React 项目中添加 react-redux 状态管理

    redux作为一个单独的库 他可以搭配 Angular vue react以及jQuery使用 今天我们再项目中搭配react redux来使用 Redux的核心概念其实很简单 将需要修改的state都存入到store里 发起一个actio
  • java中设计思想及设计原则及设计模式解析

    前言 在java开发过程中 或者各种语言在编程过程中 对基础知识的累计 这其中就会包含java的设计思想及设计原则及设计模式 我们想对当前的架构以及某些功能逻辑进行优化 及繁简 更加适合当前业务的开发 如何让人一看就能懂得代码 以及各种热门
  • 10071 - Back to High School Physics

    Back to High School Physics Time limit 3 000 seconds A partical 电 质点 has inital velocity and accerleration 初速度与加速度 If it
  • SQL6 查找学校是北大的学生信息

    描述 题目 现在运营想要筛选出所有北京大学的学生进行用户调研 请你从用户信息表中取出满足条件的数据 结果返回设备id和学校 示例 user profile id device id gender age university provinc
  • Linux--消息队列

    目录 一 线程和进程的概念 1 线程 2 进程 3 ipcs 二 消息队列代码 写消息 man msgget返回值问题 添加消息队列 man msgsnd 运行代码 运行结果图 进程和消息的状态 三 消息队列代码 读消息 b c读取消息 执
  • 微信公众号html教程,公众号排版简易教程

    微信公众号文章排版流程 1 将文章转换成Markdown格式 2 使用Typora渲染Markdown格式 3 使用印象笔记 生成图表 流程图 时序图等 并转换成图片 4 将Markdown格式转换为Html格式 5 将Html格式内容复制
  • 企业微信开始代开发应用 配置代开发应用回调URL

    用到的php版本是7 4 开发时间 2022 11 2 在做公司的项目时因为企业微信的更新 同一个服务器ip不能在多个企业项目中出现否则就会被判定为第三方服务商 所以新建应用时只能新建代开发应用 在配置回调的时候走了很多坑 在这里梳理一下
  • Dialog DA14585——基本硬件资源配置

    GPIO DA14585共有3中类型的封装 WLCSP34pin QFN40pin QFN48pin 除去专用的RST GND XTAL ANT VBAT脚后 分别有14 25 32个IO口可用 目前最常用的封装类型是QFN40 处理单元
  • ssh key创建本地和git的安全连接 && ssh key的创建与github/gitlab中ssh key的添加

    如未创建ssh key会报该问题 The authenticity of host can t be established Permission denied publickey ssh key的作用 创建本地和gitlab的安全连接 S
  • 从特效入手,深入了解CSS(二):鼠标 hover 特效

    不建议跳跃阅读 这篇文章将从头开始介绍如何实现一个特效 中间偶尔会穿插一些css3或平时接触不多的css属性 首先看一看这一期的特效 HTML部分 h1 Hover Me h1 CSS 部分 首先通过grid布局的place content
  • 一文搞定java8中的重要特性

    文章目录 Lambda表达式 函数式接口 四大内置函数接口 接口的默认方法和静态方法 方法引用 重复注解 更好的类型推断 拓宽注解的应用场景 Optional Stream Date Time API Base64 并行数组 并发性 JVM