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接⼝流程
- 定义⼀个函数式接⼝ 需要标注此接⼝ @FunctionalInterface,否则万⼀团队成员在接⼝上加 了其他⽅法则容易出故障
- 编写⼀个⽅法,输⼊需要操做的数据和接⼝
- 在调⽤⽅法时传⼊数据 和 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