JDK8 新特性介绍

JDK8之default关键字介绍

  • 创建⼀个java基础项⽬
  • 在jdk1.8以前接⼝⾥⾯是只能有抽象⽅法,不能有任何⽅法的实现的
  • jdk1.8⾥⾯打破了这个规定,引⼊了新的关键字default,使⽤default修饰⽅法,可以在接⼝⾥⾯定义具体的⽅法实现
  • 默认⽅法: 接⼝⾥⾯定义⼀个默认⽅法,这个接⼝的实现类实现了这个接⼝之后,不⽤管这个 default修饰的⽅法就可以直接调⽤,即接⼝⽅法的默认实现
public interface Animal {
 void run();
 void eat();
 default void breath(){
     System.out.println("使⽤氧⽓呼吸");
 }
}
  • 静态⽅法: 接⼝名.静态⽅法来访问接⼝中的静态⽅法
public interface Animal {
 void run();

 void eat();

 default void breath(){
     System.out.println("使⽤氧⽓呼吸");
 }

 static void test(){
     System.out.println("这是静态⽅法");
 }
}

 

JDK8之新增base64加解密API

  • 什么是Base64编码?

Base64是⽹络上最常⻅的⽤于传输8Bit字节码的编码⽅式之⼀,Base64就是⼀种基于64个可打印字符来表示⼆进制数据的⽅法,基于64个字符A-Z,a-z,0-9,+,/ 的编码⽅式, 是⼀种能将任意⼆进制数据⽤64种字元组合成字符串的⽅法,⽽这个⼆进制数据和字符串资料之 间是可以互相转换的,在实际应⽤上,Base64除了能将⼆进制数据可视化之外,也常⽤来表示字 串加密过后的内容

  • 早期java要使⽤Base64怎么做

使⽤JDK⾥sun.misc套件下的BASE64Encoder和BASE64Decoder这两个类

 BASE64Encoder encoder = new BASE64Encoder();
 BASE64Decoder decoder = new BASE64Decoder();
 String text = "vincent你好";
 byte[] textByte = text.getBytes("UTF-8");
 //编码
 String encodedText = encoder.encode(textByte);
 System.out.println(encodedText);
 //解码
 System.out.println(new String(decoder.decodeBuffer(encodedText),"UTF-8"));

缺点:编码和解码的效率⽐较差,公开信息说以后的版本会取消这个⽅法

Apache Commons Codec有提供Base64的编码与解码

缺点:是需要引⽤Apache Commons Codec

  • jdk1.8之后怎么玩?(⾸选推荐)

Jdk1.8的java.util包中,新增了Base64的类

 Base64.Decoder decoder = Base64.getDecoder();
 Base64.Encoder encoder = Base64.getEncoder();
 String text = "vincent你好";
 byte[] textByte = text.getBytes("UTF-8");
 //编码
 String encodedText = encoder.encodeToString(textByte);
 System.out.println(encodedText);
 //解码
 System.out.println(new String(decoder.decode(encodedText), "UTF8"));

好处:不⽤引包,编解码效率远⼤于 sun.misc 和 Apache Commons Codec

 

JDK8之时间⽇期处理类

  • 时间处理再熟悉不过,SimpleDateFormat,Calendar等类

旧版缺点: java.util.Date 是⾮线程安全 的 API设计⽐较差,⽇期/时间对象⽐较,加减麻烦

  • Java 8通过发布新的 Date-Time API (JSR 310)来进⼀步加强对⽇期与时间的处理

新增了很多常⻅的api,如⽇期/时间的⽐较,加减,格式化等 ,包所在位置 java.time

核⼼类

LocalDate:不包含具体时间的⽇期

LocalTime:不含⽇期的时间

LocalDateTime:包含了⽇期及时间

  • LocalDate 常⽤API
LocalDate today = LocalDate.now();
System.out.println("今天⽇期:" + today);

//获取年,⽉,⽇,周⼏
System.out.println("现在是哪年:"+today.getYear());
System.out.println("现在是哪⽉:"+today.getMonth());
System.out.println("现在是哪⽉(数字):"+today.getMonthValue());
System.out.println("现在是⼏号:"+today.getDayOfMonth());
System.out.println("现在是周⼏:"+today.getDayOfWeek());

//加减年份, 加后返回的对象才是修改后的, 旧的依旧是旧的
LocalDate changeDate = today.plusYears(1);
System.out.println("加后是哪年:"+changeDate.getYear());
System.out.println("旧的是哪年:"+today.getYear());

//⽇期⽐较
System.out.println("isAfter: "+changeDate.isAfter(today));
//getYear() int 获取当前⽇期的年份
//getMonth() Month 获取当前⽇期的⽉份对象
//getMonthValue() int 获取当前⽇期是第⼏⽉
//getDayOfWeek() DayOfWeek 表示该对象表示的⽇期是星期⼏
//getDayOfMonth() int 表示该对象表示的⽇期是这个⽉第⼏天
//getDayOfYear() int 表示该对象表示的⽇期是今年第⼏天
//withYear(int year) LocalDate 修改当前对象的年份
//withMonth(int month) LocalDate 修改当前对象的⽉份
//withDayOfMonth(int dayOfMonth) LocalDate 修改当前对象在当⽉的⽇期
//plusYears(long yearsToAdd) LocalDate 当前对象增加指定的年份数
//plusMonths(long monthsToAdd) LocalDate 当前对象增加指定的⽉份数
//plusWeeks(long weeksToAdd) LocalDate 当前对象增加指定的周数
//plusDays(long daysToAdd) LocalDate 当前对象增加指定的天数
//minusYears(long yearsToSubtract) LocalDate 当前对象减去指定的年数
//minusMonths(long monthsToSubtract) LocalDate 当前对象减去注定的⽉数
//minusWeeks(long weeksToSubtract) LocalDate 当前对象减去指定的周数
//minusDays(long daysToSubtract) LocalDate 当前对象减去指定的天数
//compareTo(ChronoLocalDate other) int ⽐较当前对象和other对象在时间上的⼤⼩,返回值如果为正,则当前对象时间较晚,
//isBefore(ChronoLocalDate other) boolean ⽐较当前对象⽇期是否在other对象⽇期之前
//isAfter(ChronoLocalDate other) boolean ⽐较当前对象⽇期是否在other对象⽇期之后
//isEqual(ChronoLocalDate other) boolean ⽐较两个⽇期对象是否相等
  • ⽇期时间格式化

JDK8之前:SimpleDateFormat来进⾏格式化,但SimpleDateFormat并不是线程安全的

JDK8之后:引⼊线程安全的⽇期与时间DateTimeFormatter

LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String ldtStr = dtf.format(ldt);
System.out.println(ldtStr);
  • 获取指定的⽇期时间对象
LocalDateTime ldt = LocalDateTime.of(2020, 09, 26, 11, 11, 30);
System.out.println(ldt);
  • 计算⽇期时间差 java.time.Duration
 LocalDateTime today = LocalDateTime.now();
 System.out.println(today);
 LocalDateTime changeDate = LocalDateTime.of(2020,10,1,10,40,30);
 System.out.println(changeDate);
 Duration duration = Duration.between(today,changeDate);//第⼆个参数减第⼀个参数
 System.out.println(duration.toDays());//两个时间差的天数
 System.out.println(duration.toHours());//两个时间差的⼩时数
 System.out.println(duration.toMinutes());//两个时间差的分钟数
 System.out.println(duration.toMillis());//两个时间差的毫秒数
 System.out.println(duration.toNanos());//两个时间差的纳秒数

 

JDK8之Optional类

  • Optional 类有啥⽤

主要解决的问题是空指针异常(NullPointerException)

怎么解决?

本质是⼀个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空

  • 创建Optional类

of():null 值作为参数传递进去,则会抛异常

Optional opt = Optional.of(user);

ofNullable():如果对象既可能是 null 也可能是⾮ null,应该使⽤ ofNullable() ⽅法

Optional opt = Optional.ofNullable(user);

  • 访问 Optional 对象的值

get() ⽅法

Optional<Student> opt = Optional.ofNullable(student);
Student s = opt.get();

如果值存在则isPresent()⽅法会返回true,调⽤get()⽅法会返回该对象⼀般使⽤get之前需要 先验证是否有值,不然还会报错

public static void main(String[] args) {
 Student student = null;
 test(student);
}
public static void test(Student student){
 Optional<Student> opt = Optional.ofNullable(student);
 System.out.println(opt.isPresent());
}
  • 兜底 orElse⽅法

orElse()如果有值则返回该值,否则返回传递给它的参数值

Student student1 = null;
Student student2 = new Student(2);
Student result = Optional.ofNullable(student1).orElse(student2);
System.out.println(result.getAge());
Student student = null;
int result = Optional.ofNullable(student).map(obj->obj.getAge()).orElse(4);
System.out.println(result);

 

JDK8 Lambda表达式

  • 什么是函数式编程和什么是lambda表达式?

在JDK8之前,Java是不⽀持函数式编程的,所谓的函数编程,即可理解是将⼀个函数(也称为“⾏ 为”)作为⼀个参数进⾏传递, ⾯向对象编程是对数据的抽象(各种各样的POJO类),⽽函数式编 程则是对⾏为的抽象(将⾏为作为⼀个参数进⾏传递)

  • java创建线程再熟悉不过了

jdk8之前创建线程

new Thread(new Runnable() {
     @Override
     public void run() {
         System.out.println("程序让世界变得更美好!!!!");
     }
 });

jdk8之后Lambda表达式则只需要使⽤⼀句话

 new Thread(() -> System.out.println("程序让世界变得更美好!!!!"));
  • 集合容器⾥⾯的字符串排序   

使⽤前

List<String> list = Arrays.asList("aaa", "ggg", "ffff", "ccc");
Collections.sort(list, new Comparator<String> () {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});
for (String string: list) {
    System.out.println(string);
}

使⽤后

List<String> list = Arrays.asList("aaa", "ggg", "ffff", "ccc");
Collections.sort(list, (a, b) - > b.compareTo(a));
for (String string: list) {
    System.out.println(string);
}
  • lambda表达式 使⽤场景(前提)

⼀个接⼝中只包含⼀个⽅法,则可以使⽤Lambda表达式,这样的接⼝称之为“函数接⼝” 语法: (params) -> expression

第⼀部分为括号内⽤逗号分隔的形式参数,参数是函数式接⼝⾥⾯⽅法的参数;第⼆部分为⼀个箭
头符号:->;第三部分为⽅法体,可以是表达式和代码块

参数列表 :
 括号中参数列表的数据类型可以省略不写
 括号中的参数只有⼀个,那么参数类型和()都可以省略不写

⽅法体:
 如果{}中的代码只有⼀⾏,⽆论有返回值,可以省略{},return,分号,要⼀起省略,其他则需要加上

好处: Lambda 表达式的实现⽅式在本质是以匿名内部类的⽅式进⾏实现

 

JDK8之⾃定义函数式编程

⾃定义lambda接⼝流程

  1. 定义⼀个函数式接⼝ 需要标注此接⼝ @FunctionalInterface,否则万⼀团队成员在接⼝上加 了其他⽅法则容易出故障
  2. 编写⼀个⽅法,输⼊需要操做的数据和接⼝
  3. 在调⽤⽅法时传⼊数据 和 lambda 表达式,⽤来操作数据
  • 需求,定义⼀个可以使⽤加减乘除的接⼝

以前需要定义4个⽅法,使⽤Lambda表达式后

@FunctionalInterface
public interface OperFunction<R,T> {
 R operator(T t1, T t2);
}
public static void main(String[] args) throws Exception {
    System.out.println(operator(20, 5, (Integer x, Integer y) - > {
        return x * y;
    }));
    System.out.println(operator(20, 5, (x, y) - > x + y));
    System.out.println(operator(20, 5, (x, y) - > x - y));
    System.out.println(operator(20, 5, (x, y) - > x / y));
}
public static Integer operator(Integer x, Integer y,
    OperFunction<Integer, Integer> of ) {
    return of.operator(x, y);
}

 

JDK8之函数式编程 Function

Lambda表达式必须先定义接⼝,创建相关⽅法之后才可使⽤,这样做⼗分不便,其实java8已经 内置了许多接⼝, 所以⼀般很少会由⽤户去定义新的函数式接⼝

Java8的最⼤特性就是函数式接⼝,所有标注了@FunctionalInterface注解的接⼝都是函数式接⼝

Java8 内置的四⼤核⼼函数式接⼝:
Consumer<T> : 消费型接⼝:有⼊参,⽆返回值
 void accept(T t);

Supplier<T> : 供给型接⼝:⽆⼊参,有返回值
 T get();

Function<T, R> : 函数型接⼝:有⼊参,有返回值
 R apply(T t);

Predicate<T> : 断⾔型接⼝:有⼊参,有返回值,返回值类型确定是boolean
 boolean test(T t);
  • Function

传⼊⼀个值经过函数的计算返回另⼀个值

T:⼊参类型,R:出参类型

调⽤⽅法:R apply(T t)

//@param <T> the type of the input to the function
//@param <R> the type of the result of the 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);
}
  • 作⽤:将转换逻辑提取出来,解耦合

不要看过于复杂,就是⼀个接⼝,下⾯是⾃定义实现

public class FunctionObj implements Function {
 @Override
 public Object apply(Object o) {
     return o+"经过apply处理拼接上了";
 }
}
  • 常规使⽤
// 输出⼊参的10倍
Function<Integer, Integer> func = p -> p * 100;
func.apply(100);

 

JDK8之函数式编程 BiFunction

  • BiFunction

Function只能接收⼀个参数,如果要传递两个参数,则⽤ BiFunction

@FunctionalInterface
public interface BiFunction<T, U, R> {
 R apply(T t, U u);
}
  • 需求: 两个数的四则运算实现
public static void main(String[] args) {
    System.out.println(operator(10, 21, (a, b) - > a + b));
    System.out.println(operator(10, 2, (a, b) - > a - b));
    System.out.println(operator(8, 4, (a, b) - > a * b));
    System.out.println(operator(10, 2, (a, b) - > a / b));
}
public static Integer operator(Integer a, Integer b, BiFunction<Integer,
    Integer, Integer> bf) {
    return bf.apply(a, b);
}

 

JDK8之函数式编程 Consumer

  • Consumer 消费型接⼝

有⼊参,⽆返回值

  • 将 T 作为输⼊,不返回任何内容

调⽤⽅法:void accept(T t);

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
  • ⽤途

因为没有出参,常⽤于打印、发送短信等消费动作

public static void main(String[] args) throws Exception {
        Consumer<String> consumer = obj - > {
            System.out.println(obj);
            System.out.println("调⽤短信接⼝发送短信,或者打印⽇志");
        };
        // sendMsg("8888888",obj->{
        // System.out.println(obj);
        // System.out.println("调⽤短信接⼝发送短信,或者打印⽇志");
        // });
        sendMsg("8888888",consumer);
     }
     public static void sendMsg(String phone,Consumer<String> consumer){
         consumer.accept(phone);
     }
  • 典型应⽤,集合的foreach
List<String> list = Arrays.asList("aaa","bbb");
list.forEach(obj->{
    //TODO
});

 

JDK8之函数式编程 Supplier

  • Supplier: 供给型接⼝

⽆⼊参,有返回值

T:出参类型;没有⼊参

调⽤⽅法:T get();

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
  • ⽤途:

泛型⼀定和⽅法的返回值类型是⼀种类型,如果需要获得⼀个数据,并且不需要传⼊参数,可 以使⽤Supplier接⼝,例如 ⽆参的⼯⼚⽅法,即⼯⼚设计模式创建对象,简单来说就是 提供者

public static void main(String[] args) {
    //Student student = new Student();
    Student student = newStudent();
    System.out.println(student.getName());
}
public static Student newStudent() {
    Supplier <Student> supplier = () - > {
        Student student = new Student();
        student.setName("默认名称");
        return student;
    };
    return supplier.get();
}

class Student {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

 

JDK8之函数式编程 Predicate

  • Predicate: 断⾔型接⼝

有⼊参,有返回值,返回值类型确定是boolean

  • T:⼊参类型;出参类型是Boolean

调⽤⽅法:boolean test(T t);

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}
  • ⽤途

接收⼀个参数,⽤于判断是否满⾜⼀定的条件,过滤数据

public static void main(String[] args) {
    List<String> list = Arrays.asList("awewrwe", "vdssdsd", "aoooo", "psdddsd");
    List<String> results = filter(list, obj - > obj.startsWith("a"));
    System.out.println(results);
}
public static List<String> filter(List<String> list,Predicate<String> predicate){
    List<String> results = new ArrayList<> ();
    for (String str: list) {
        if (predicate.test(str)) {
            results.add(str);
        }
    }
    return results;
}

 

JDK8之⽅法与构造函数引⽤

  • 以前⽅法调⽤   对象.⽅法名 或者 类名.⽅法名
  • jdk1.8提供了另外⼀种调⽤⽅式 

说明:⽅法引⽤是⼀种更简洁易懂的lambda表达式,操作符是双冒号::,⽤来直接访问类或者实例
已经存在的⽅法或构造⽅法

通过⽅法引⽤,可以将⽅法的引⽤赋值给⼀个变量

语法:左边是容器(可以是类名,实例名),中间是"::",右边是相应的⽅法名

静态⽅法,则是ClassName::methodName。如 Object::equals
实例⽅法,则是Instance::methodName
构造函数,则是 类名::new;

单个参数
Function<⼊参1, 返回类型> func = ⽅法引⽤
应⽤ func.apply(⼊参);

2个参数
BiFunction<⼊参1,⼊参2, 返回类型> func = ⽅法引⽤
应⽤ func.apply(⼊参1,⼊参2);

public class TestJdk8 {
    public static void main(String[] args) {

        // 使⽤双冒号::来构造静态函数引⽤
        Function<String, Integer> fun = Integer::parseInt;
        Integer value = fun.apply("1024");
        System.out.println(value);

        // 使⽤双冒号::来构造⾮静态函数引⽤
        String content = "欢迎来到vincent的程序天地";
        Function<Integer, String> func = content::substring;
        String result = func.apply(1);
        System.out.println(result);

        // 构造函数引⽤,多个参数
        BiFunction<String, Integer, User> biFunction = User::new;
        User user1 = biFunction.apply("Vincent", 18);
        System.out.println(user1.toString());

        //构造函数引⽤,单个参数
        Function<String, User> function = User::new;
        User user2 = function.apply("vincent");
        System.out.println(user2.toString());

        // 函数引⽤也是⼀种函数式接⼝,可以将函数引⽤作为⽅法的参数
        sayHello(String::toUpperCase, "baidu.com");
    }
    /**
     *
     * @param func 函数引⽤
     * @param param 对应的参数
     */
    private static void sayHello(Function<String, String> func, String param) {
        String result = func.apply(param);
        System.out.println(result);
    }
}
class User {
    private String username;
    private Integer age;
    public User() {

    }
    public User(String username) {
        this.username = username;
    }
    public User(String username, Integer age) {
        this.username = username;
        this.age = age;
    }
}

 

JDK8之流Stream介绍

  • 什么是stream
  • Stream 中⽂称为 “流”,通过将集合转换为这么⼀种叫做 “流”的元素队列,通过声明性⽅式, 能够对集合中的每个元素进⾏⼀系列并⾏或串⾏的流⽔线操作
  • 元素是特定类型的对象,所以元素集合看作⼀种流, 流在管道中传输, 且可以在管道的节点 上进⾏处理, ⽐如 排序,聚合,过滤等操作

  • 操作详情
  • 数据元素便是原始集合, 如List、 Set、 Map等⽣ 成流, 可以是串⾏ 流stream() 或者并⾏ 流 parallelStream()
  • 中间操作, 可以是 排序, 聚合, 过滤, 转换等
  • 终端操作, 很多流操作本身就会返回⼀ 个流, 所以多个操作可以直接连接起来, 最后统⼀ 进⾏ 收集
  • 快速上⼿
List<String> list = Arrays.asList("springboot教程", "微服务教程", "并发编程 ","压⼒测试 ","架构课程 ");
List<String> resultList = list.stream().map(obj - > "在知识的海洋中学: "+obj).collect(Collectors.toList());
System.out.println(resultList);

 

JDK8之流操作map和filter函数

  • map函数
  • 将流中的每⼀个元素 T 映射为 R(类似类型转换)
  • 上面的例⼦就是,类似遍历集合,对集合的每个对象做处理
  • 场景:转换对象,如java web开发中集合⾥⾯的DO对象转换为DTO对象
List<String> list = Arrays.asList("springboot教程", "微服务教程", "并发编程 ","压⼒测试 ","架构课程 ");
List<String> resultList = list.stream().map(obj - > "在知识的海洋中学: "+obj).collect(Collectors.toList());
System.out.println(resultList);
List<User> list = Arrays.asList(new User(1, "⼩东", "123"), new User(21, "jack", "rawer"),
new User(155, "tom", "sadfsdfsdfsd"),
new User(231, "marry", "234324"), 
new User(100, "vincent","122223 "));
List<UserDTO> userDTOList = list.stream().map(obj - > {
    UserDTO userDTO = new UserDTO(obj.getId(), obj.getName());
    return userDTO;
}).collect(Collectors.toList()); 
System.out.println(userDTOList);
  • filter函数

⽤于通过设置的条件过滤出元素

需求:过滤出字符串⻓度⼤于5的字符串

List<String> list = Arrays.asList("springboot", "springcloud","redis", "git", "netty", "java", "html", "docker");

List<String> resultList = list.stream().filter(obj -> obj.length() >
5).collect(Collectors.toList());

System.out.println(resultList);

场景:主要⽤于筛选过滤出符合条件的元素

 

JDK8之流操作limit和sorted函数

  • sorted函数

sorted() 对流进⾏⾃然排序, 其中的元素必须实现 Comparable 接⼝

List<String> list = Arrays.asList("springboot", "springcloud","redis", "git", "netty", "java", "html", "docker");

List<String> resultList = list.stream().sorted().collect(Collectors.toList());
  • sorted(Comparator comparator) ⽤来⾃定义升降序
List<String> list = Arrays.asList("springboot", "springcloud","redis", "git", "netty", "java", "html", "docker");

//根据⻓度进⾏排序
//List<String> resultList = list.stream().sorted(Comparator.comparing(obj ->
//obj.length())).collect(Collectors.toList());

//List<String> resultList = list.stream().sorted(Comparator.comparing(obj ->
//obj.length(),Comparator.reverseOrder())).collect(Collectors.toList());

List<String> resultList =
list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.toList());

System.out.println(resultList);
  • limit函数

截断流使其最多只包含指定数量的元素

List<String> list = Arrays.asList("springboot", "springcloud","redis", "git", "netty", "java", "html", "docker");

//limit截取
List<String> resultList =
list.stream().sorted(Comparator.comparing(String::length).reversed()).limit(3).collect(Collectors.toList());

System.out.println(resultList);

 

JDK8之流操作allMatch和anyMatch函数

  • allMatch函数

检查是否匹配所有元素,只有全部符合才返回true

List<String> list = Arrays.asList("springboot", "springcloud", "redis","git", "netty", "java", "html", "docker");

boolean flag = list.stream().allMatch(obj->obj.length()>1);
System.out.println(flag);
  • anyMatch函数

检查是否⾄少匹配⼀个元素,如果存在,则返回true

List<String> list = Arrays.asList("springboot", "springcloud", "redis","git", "netty", "java", "html", "docker");

boolean flag = list.stream().anyMatch(obj->obj.length()>10);
System.out.println(flag);

 

JDK8之流操作max和min函数

  • max和min函数

最⼤值和最⼩值

List<Student> list = Arrays.asList(new Student(32),new
Student(33),new Student(21),new Student(29),new Student(18));

//list.stream().max(Comparator.comparingInt(Student::getAge));

//最⼤
Optional<Student> optional = list.stream().max((s1, s2)-
>Integer.compare(s1.getAge(),s2.getAge()));

//最⼩
Optional<Student> optional = list.stream().min((s1, s2)-
>Integer.compare(s1.getAge(),s2.getAge()));

System.out.println(optional.get().getAge());

 

JDK8之并⾏流 parallelStream

为什么会有这个并⾏流?

集合做重复的操作,如果使⽤串⾏执⾏会相当耗时,因此⼀般会采⽤多线程来加快, Java8的 paralleStream⽤ fork/join 框架提供了并发执⾏能⼒

  • 底层原理
  • 线程池(ForkJoinPool)维护⼀个线程队列
  • 可以分割任务,将⽗任务拆分成⼦任务,完全贴合分治思想
  • 两个区别
//顺序输出
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.stream().forEach(System.out::println);

//并⾏乱序输出
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream().forEach(System.out::println);
  • 问题

paralleStream 并⾏是否⼀定⽐Stream串⾏快?

错误,数据量少的情况,可能串⾏更快,ForkJoin会耗性能

多数情况下并⾏⽐串⾏快,是否可以都⽤并⾏?

不⾏,部分情况会有线程安全问题,parallelStream⾥⾯使⽤的外部变量,⽐如集合⼀ 定要使⽤线程安全集合,不然就会引发多线程安全问题

 

JDK8之reduce操作

什么是reduce操作?

聚合操作,中⽂意思是 “减少”

根据⼀定的规则将Stream中的元素进⾏计算后返回⼀个唯⼀的值

  • 常⽤⽅法⼀
Optional<T> reduce(BinaryOperator<T> accumulator);

accumulator 计算的累加器

例⼦: 第⼀个元素相加和第⼆个元素相加,结果再和第三个元素相加,直到全部相加完成

int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1+ item2).get();

不⽤lambda的写法

int result = Stream.of(1,2,3,4,5).reduce(new BinaryOperator<Integer>() {
 @Override
 public Integer apply(Integer item1, Integer item2) {
     return item1 + item2;
 }
}).get();
  • 常⽤⽅法⼆
T reduce(T identity, BinaryOperator<T> accumulator);

identity  ⽤户提供⼀个循环计算的初始值

accumulator 计算的累加器

例⼦: 100作为初始值,然后和第⼀个元素相加,结果在和第⼆个元素相加,直到全部 相加完成

int value = Stream.of(1, 2, 3, 4,5).reduce(100, (sum, item) -> sum + item);

  • 需求 : 求最⼤值
int value = Stream.of(1645, 234345, 32,44434,564534,435,34343542,212).reduce( (item1, item2) -> item1 >item2 ? item1 : item2 ).get();

System.out.println(value);

 

JDK8之集合的foreach

  • 集合遍历的⽅式
  • for循环
  • 迭代器 Iterator
  • Jdk8⾥⾯的新增接⼝
default void forEach(Consumer<? super T> action) {
     Objects.requireNonNull(action);
     for (T t : this) {
         action.accept(t);
     }
}
List<Student> results = Arrays.asList(new Student(32),new Student(33),new Student(21),new Student(29),new Student(18));
results.forEach(obj->{
 System.out.println(obj.toString());
});
  • 注意点

不能修改包含外部的变量的值

不能⽤break或者return或者continue等关键词结束或者跳过循环

 

JDK8 收集器和集合统计

  • collect()⽅法的作⽤

⼀个终端操作, ⽤于对流中的数据进⾏归集操作,collect⽅法接受的参数是⼀个Collector

有两个重载⽅法,在Stream接⼝⾥⾯

 //重载⽅法⼀
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R>combiner);

 //重载⽅法⼆
 <R, A> R collect(Collector<? super T, A, R> collector);
  • Collector的作⽤

就是收集器,也是⼀个接⼝, 它的⼯具类Collectors提供了很多⼯⼚⽅法

  • Collectors 的作⽤

⼯具类,提供了很多常⻅的收集器实现

Collectors.toList()

public static <T> Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>((Supplier<List<T>>)
        ArrayList::new, List::add,(left, right) -> {
        left.addAll(right); return left; }, CH_ID);
}

ArrayList::new,创建⼀个ArrayList作为累加器

List::add,对流中元素的操作就是直接添加到累加器中

reduce 操作, 对⼦任务归集结果addAll,后⼀个⼦任务的结果直接全部添加到 前⼀个⼦任务结果中

CH_ID 是⼀个unmodifiableSet集合

Collectors.toMap()

Collectors.toSet()

Collectors.toCollection() :⽤⾃定义的实现Collection的数据结构收集

Collectors.toCollection(LinkedList::new)

Collectors.toCollection(CopyOnWriteArrayList::new)

Collectors.toCollection(TreeSet::new)

 

JDK8之joining函数

  • 拼接函数 Collectors.joining
//3种重载⽅法
Collectors.joining()
Collectors.joining("param")
Collectors.joining("param1", "param2", "param3")
  • 其中⼀个的实现
public static Collector<CharSequence, ?, String> joining() {
 return new CollectorImpl<CharSequence, StringBuilder, String>(
     StringBuilder::new, StringBuilder::append,
     (r1, r2) -> { r1.append(r2); return r1; },
     StringBuilder::toString, CH_NOID);
 }
  • 说明

该⽅法可以将Stream得到⼀个字符串, joining函数接受三个参数,分别表示  元素之间的连接符、前缀和后缀

String result = Stream.of("springboot", "mysql", "html5","css3").collect(Collectors.joining(",", "[", "]"));

 

JDK8之收集器 partitioningBy分组

  • Collectors.partitioningBy 分组,key是boolean类型
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
    return partitioningBy(predicate, toList());
}
  • 练习: 根据list⾥⾯进⾏分组,字符串⻓度⼤于4的为⼀组,其他为另外⼀组
List<String> list = Arrays.asList("java", "springboot","HTML5","nodejs","CSS3");

Map<Boolean, List<String>> result = list.stream().collect(partitioningBy(obj -> obj.length() > 4));

 

JDK8之收集器 group by分组

  • 分组 Collectors.groupingBy()
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<?
super T, ? extends K> classifier) { 
    return groupingBy(classifier, toList());
}
  • 练习:根据学⽣所在的省份,进⾏分组
List<Student> students = Arrays.asList(new Student("⼴东", 23), new Student("⼴东", 24), new Student("⼴东", 23), new Student("北京", 22), new Student("北京", 20), new Student("北京", 20), new Student("海南", 25));

Map <String, List<Student>> listMap =
    students.stream().collect(Collectors.groupingBy(obj - >
        obj.getProvince()));

listMap.forEach((key, value) - > {
    System.out.println("========");
    System.out.println(key);
    value.forEach(obj - > {
        System.out.println(obj.getAge());
    });

});

class Student {
    private String province;
    private int age;
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Student(String province, int age) {
        this.age = age;
        this.province = province;
    }
}
  • 分组统计

聚合函数进⾏统计查询,分组后统计个数

Collectors.counting() 统计元素个数

public static <T, K, A, D> Collector<T, ?, Map<K, D>>
groupingBy(Function<? super T, ? extends K> classifier,Collector<? super
T, A, D> downstream) {
    return groupingBy(classifier, HashMap::new, downstream);
}
  • 需求:统计各个省份的⼈数
List<Student> students = Arrays.asList(new Student("⼴东", 23), new
Student("⼴东", 24), new Student("⼴东", 23),new Student("北京", 22), new
Student("北京", 20), new Student("北京", 20),new Student("海南", 25));

Map<String, Long> listMap =
students.stream().collect(Collectors.groupingBy(Student::getProvince,Collectors.counting()));

listMap.forEach((key, value) -> {System.out.println(key+"省⼈数有"+value);});

 

JDK8之summarizing集合统计

  • summarizing 统计相关, summarizingInt的源码
public static <T> Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) { 
return new CollectorImpl<T, IntSummaryStatistics, IntSummaryStatistics>(
 IntSummaryStatistics::new,(r, t) -> r.accept(mapper.applyAsInt(t)),
 (l, r) -> { l.combine(r); return l; }, CH_ID);
}
  • 作⽤

可以⼀个⽅法把统计相关的基本上都完成

  • 分类

summarizingInt

summarizingLong

summarizingDouble

  • 需求:统计学⽣的各个年龄信息
 List < Student > students = Arrays.asList(new Student("⼴东", 23), new Student("⼴东", 24), new Student("⼴东", 23), new Student("北京", 22), new Student("北京", 20), new Student("北京", 20), new Student("海南", 25));

 IntSummaryStatistics summaryStatistics =students.stream().collect(Collectors.summarizingInt(Student::getAge));

 System.out.println("平均值:" + summaryStatistics.getAverage());
 System.out.println("⼈数:" + summaryStatistics.getCount());
 System.out.println("最⼤值:" + summaryStatistics.getMax());
 System.out.println("最⼩值:" + summaryStatistics.getMin());
 System.out.println("总和:" + summaryStatistics.getSum());

 class Student {
     private String province;
     private int age;
     public String getProvince() {
         return province;
     }
     public void setProvince(String province) {
         this.province = province;
     }
     public int getAge() {
         return age;
     }
     public void setAge(int age) {
         this.age = age;
     }
     public Student(String province, int age) {
         this.age = age;
         this.province = province;
     }
 }

 

JDK8 新的内存空间Matespace

JVM 种类有很多,⽐如 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM,我们讲的是 Hotspot才有,JRockit以及J9是没有这个区域

  • JVM内存知识

在JDK8之前的HotSpot JVM,有个区域叫做“永久代(permanent generation), 通过 在命令⾏设置参数-XX:MaxPermSize来设定永久代最⼤可分配的内存空间   如果JDK8⾥⾯设置了PermSize 和 MaxPermSize 会被忽略并给出警告

  • 作⽤

该块内存主要是被JVM⽤来存放 class 和 mate 信息的,当 class 被加载 loader 的时候就会 被存储到该内存区中,如⽅法的编译信息及字节码、常量池和符号解析、类的层级信息,字段,名 字等

  • jdk8的修改

JDK8 HotSpot JVM 使⽤本地内存来存储类元数据信息,叫做 元空间(Metaspace) 在默认情况下Metaspace的⼤⼩只与本地内存⼤⼩有关

常⽤的两个参数

  • -XX:MetaspaceSize=N

指Metaspace扩容时触发FullGC的初始化阈值

  • -XX:MaxMetaspaceSize=N

指⽤于限制Metaspace增⻓的上限,防⽌因为某些情况导致 Metaspace⽆限的使⽤本地内存

不管两个参数如何设置,都会从20.8M开始,然后随着类加载越来越多不断扩容调整直到最⼤

  • 查看⼤⼩

jstat -gc pid MC: current metaspace capacity MU: mateaspace utilization  单位是KB

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值