菜鸟的java8新特性探索
说明
更新时间:2020/8/16 17:26,更新了Optional
更新时间:2020/5/29 11:11,更新了基本功能
关于java8新特性,很久之前就听说过了,现在将java8新特性中的Stream api的使用记录在这里,以便日后查看,本文会持续更新,不断地扩充
本文仅为记录学习轨迹,如有侵权,联系删除
一、关于java8新特性
Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。
其中个人u觉得最重要的新特性Stream API,个人使用的也比较频繁,虽然当时不知道这是java8的新特性,Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
二、Stream
Stream流使用流程:开始---->中间操作---->终止操作
开始包括流的创建,中间操作包括对创建的流进行筛选、聚合等操作,终止操作则将得到的结果转为某种形式输出
/**
* Java从8开始,不但引入了Lambda表达式,还引入了一个全新的流式API:Stream API。它位于java.util.stream包中。
*
* 划重点:这个Stream不同于java.io的InputStream和OutputStream,它代表的是任意Java对象的序列。
*
* Stream API的基本用法就是:创建一个Stream,然后做若干次转换,最后调用一个求值方法获取真正计算的结果:
*
* 注意:把数组变成Stream使用Arrays.stream()方法。对于Collection(List、Set、Queue等),直接调用stream()方法就可以获得Stream。
*/
stream的创建
主要分两类,数组和集合(List、Map、Set)
//数组变成Stream使用Arrays.stream()方法
int[] nums = {1,2,3,4,5,6,7,8,9,10};
Arrays.stream(nums).forEach(n->{
//用forEach()遍历数组无法获取下标
System.out.println("n = "+n);
});
//对于Collection(List、Set、Queue等),直接调用stream()方法就可以获得Stream
List<String> list = new ArrayList<>();
list.add("灰太狼");
list.add("喜羊羊");
list.add("小灰灰");
list.add("懒羊羊");
list.stream().forEach(l->{
System.out.println("index = "+list.indexOf(l)+" value = "+l);
});
Stream的中间操作
下面整理很多常用的一些中间操作,包括求和,过滤、平均值、排序等,具体如下
可能用到的两个实体类
class User{
private Integer id;
private String name;
private Date birthday;
private Integer age;
private String sex;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", birthday=" + birthday +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
public User(Integer id, String name, Date birthday, Integer age, String sex) {
this.id = id;
this.name = name;
this.birthday = birthday;
this.age = age;
this.sex = sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public Date getBirthday() {
return birthday;
}
}
class Account {
private Integer id;
private String name;
public Account() {
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Account(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(1)forEach()
/**
* Stream中间操作 1.forEach()
* 循环每一个元素
*/
public static void StreamForEcach(){
/**
* 数组
*/
System.out.println("--------------------数组的forEach()--------------------");
int[] nums = {1,2,3,4,5,6,7,8,9,10};
Arrays.stream(nums).forEach(n->{
//用forEach()遍历数组无法获取下标
System.out.println("n = "+n);
});
/**
* List
*/
System.out.println("--------------------List的forEach()--------------------");
List<String> list = new ArrayList<>();
list.add("灰太狼");
list.add("喜羊羊");
list.add("小灰灰");
list.add("懒羊羊");
list.stream().forEach(l->{
System.out.println("index = "+list.indexOf(l)+" value = "+l);
});
/**
* map
*/
System.out.println("-------------------Map的forEach()--------------------");
Map<String,String> map = new HashMap<>();
map.put("狼1","灰太狼");
map.put("狼2","小灰灰");
map.put("羊1","喜羊羊");
map.put("羊2","懒羊羊");
//map遍历方式1
System.out.println("map遍历方式1:");
map.keySet().forEach(key -> {
System.out.println("key : "+key+" value : "+map.get(key));
});
//map遍历方式2
System.out.println("map遍历方式2:");
map.entrySet().forEach(entry -> {
System.out.println("key : " + entry.getKey() + " value : " + entry.getValue());
});
//map遍历方式3
System.out.println("map遍历方式3:");
map.forEach((key,value)->{
System.out.println("key : "+key+" value : "+value);
});
/**
* set
*/
System.out.println("--------------------set的forEach()--------------------");
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.forEach(s->{
System.out.println(s);
});
}
(2)filter()
/**
* Stream中间操作 2.filter()
* 过滤筛选出符合某些条件的项,filter(条件 && 条件)
*/
public static void StreamFilter(){
/**
* 数组
*/
System.out.println("--------------------数组的filter()--------------------");
int[] nums = {1,2,3,4,5,6,7,8,9,10};
//筛选出除了10的所有偶数
int[] newNums = Arrays.stream(nums).filter(n -> (n % 2 == 0 && n != 10)).toArray();
Arrays.stream(newNums).forEach(System.out::println);
/**
* List
*/
System.out.println("--------------------List的filter()--------------------");
List<String> list = new ArrayList<>();
list.add("灰太狼");
list.add("喜羊羊");
list.add("小灰灰");
list.add("懒羊羊");
//筛选出以"羊"结尾的项
List<String> newList = list.stream().filter(l -> l.endsWith("羊")).collect(Collectors.toList());
newList.forEach(System.out::println);
/**
* map
*/
System.out.println("--------------------Map的filter()--------------------");
Map<String,String> map = new HashMap<>();
map.put("狼1","灰太狼");
map.put("狼2","小灰灰");
map.put("羊1","喜羊羊");
map.put("羊2","懒羊羊");
//通过以狼结尾的项筛选出灰太狼
Map<String, String> newMap = map.entrySet().stream().filter(m -> {
if (m.getKey().startsWith("狼") && m.getValue().equals("灰太狼")) {
return true;
} else {
return false;
}
}).collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue()));
newMap.forEach((k,v)->{
System.out.println("key = "+k+" value = "+v);
});
/**
* set
*/
System.out.println("--------------------Set的filter()--------------------");
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
//筛选出偶数
Set<Integer> newSet = set.stream().filter(s -> s % 2 == 0).collect(Collectors.toSet());
newSet.forEach(System.out::println);
}
(3)limit()
/**
* Stream中间操作 3.limit()
* 它是用于限制流中元素的个数,即取前n个元素,返回新的流
*/
public static void StreamLimit(){
/**
* 数组
*/
System.out.println("--------------------数组的limit()--------------------");
int[] nums = {1,2,3,4,5,6,7,8,9,10};
//取出前10个数
int[] newNums = Arrays.stream(nums).limit(5).toArray();
Arrays.stream(newNums).forEach(System.out::println);
/**
* List
*/
System.out.println("--------------------List的limit()--------------------");
List<String> list = new ArrayList<>();
list.add("灰太狼");
list.add("喜羊羊");
list.add("小灰灰");
list.add("懒羊羊");
//取出前两项
List<String> newList = list.stream().limit(2).collect(Collectors.toList());
newList.forEach(System.out::println);
/**
* map
*/
System.out.println("--------------------Map的limit()--------------------");
Map<String,String> map = new HashMap<>();
map.put("狼1","灰太狼");
map.put("狼2","小灰灰");
map.put("羊1","喜羊羊");
map.put("羊2","懒羊羊");
//取出前两项
Map<String, String> newMap = map.entrySet().stream().limit(2).collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue()));
newMap.forEach((k,v)->{
System.out.println("key = "+k+" value = "+v);
});
/**
* set
*/
System.out.println("--------------------Set的limit()--------------------");
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
//取出前两项
Set<Integer> newSet = set.stream().limit(2).collect(Collectors.toSet());
newSet.forEach(System.out::println);
}
(4)skip()
/**
* Stream中间操作 4.skip()
* 该方法与limit()方法互补
* skip()方法用于跳过前面n个元素,然后再返回新的流
*/
public static void StreamSkip(){
/**
* 方法skip(n)的参数n的四种情况:
*
* (1)当n<0时,抛IllegalArgumentException异常;
*
* (2)当n=0时,相当没有跳过任何元素,原封不动、完璧归赵;
*
* (3)当0<n<length时,跳过n个元素后,返回含有剩下的元素的流;
*
* (4)当n>=length时,跳过所有元素,返回空流。
*/
/**
* 数组
*/
System.out.println("--------------------数组的skip()--------------------");
int[] nums = {1,2,3,4,5,6,7,8,9,10};
//跳过前5项返回其余的项
int[] newNums = Arrays.stream(nums).skip(5).toArray();
Arrays.stream(newNums).forEach(System.out::println);
/**
* List
*/
System.out.println("--------------------List的skip()--------------------");
List<String> list = new ArrayList<>();
list.add("灰太狼");
list.add("喜羊羊");
list.add("小灰灰");
list.add("懒羊羊");
跳过前2项返回其余的项
List<String> newList = list.stream().skip(2).collect(Collectors.toList());
newList.forEach(System.out::println);
/**
* map
*/
System.out.println("--------------------Map的skip()--------------------");
Map<String,String> map = new HashMap<>();
map.put("狼1","灰太狼");
map.put("狼2","小灰灰");
map.put("羊1","喜羊羊");
map.put("羊2","懒羊羊");
//跳过前2项返回其余的项
Map<String, String> newMap = map.entrySet().stream().skip(2).collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue()));
newMap.forEach((k,v)->{
System.out.println("key = "+k+" value = "+v);
});
/**
* set
*/
System.out.println("--------------------Set的skip()--------------------");
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
//跳过前2项返回其余的项
Set<Integer> newSet = set.stream().skip(2).collect(Collectors.toSet());
newSet.forEach(System.out::println);
}
(5)distinct()
/**
* Stream中间操作 5.distinct()
* 去除重复项
*/
public static void StreamDistinct(){
/**
* 数组
*/
System.out.println("--------------------数组的distinct()--------------------");
int[] nums = {1,2,3,1,2,34,4,5,5,8,4};
//去除重复项
int[] newNums = Arrays.stream(nums).distinct().toArray();
Arrays.stream(newNums).forEach(System.out::println);
/**
* List
*/
System.out.println("--------------------List的distinct()--------------------");
List<String> list = new ArrayList<>();
list.add("灰太狼");
list.add("灰太狼");
list.add("喜羊羊");
list.add("喜羊羊");
list.add("懒羊羊");
//去除重复项
List<String> newList = list.stream().distinct().collect(Collectors.toList());
newList.forEach(System.out::println);
/**
* map和set因为不可重复性所以没必要用到distinct()
*/
}
(6)map()
/**
* Stream中间操作 6.map()
* 可以将对象转换为其他对象
* Stream.map()是Stream最常用的一个转换方法,它把一个Stream转换为另一个Stream
* 所谓map操作,就是把一种操作运算,映射到一个序列的每一个元素上。
* 例如,对x计算它的平方,可以使用函数f(x) = x * x。我们把这个函数映射到一个序列1,2,3,4,5上,就得到了另一个序列1,4,9,16,25:
*/
public static void StreamMap(){
//将数组先转为Stream,再平方后再转为数组
int[] nums = {1, 2, 3, 4, 5};
int[] result1 = Arrays.stream(nums)
.map(n -> n * n)
.toArray();
//将Strings列表转换为大写
List<String> list1 = Arrays.asList("a","b","c","d","e");
List<String> newList1 = list1.stream().map(String::toUpperCase).collect(Collectors.toList());
newList1.forEach(System.out::println);
//对象列表 - >字符串列表
List<User> users = Arrays.asList(
new User(1,"灰太狼",new Date(),11,"男"),
new User(2,"喜羊羊",new Date(),10,"男"),
new User(3,"小灰灰",new Date(),15,"男")
);
List<String> newUsers = users.stream().map(u -> u.getName()).collect(Collectors.toList());
newUsers.forEach(System.out::println);
//对象列表 - >其他对象列表(User -> Account)
List<Account> accounts = users.stream().map(u -> {
Account account = new Account();
account.setId(u.getId());
account.setName(u.getName());
return account;
}).collect(Collectors.toList());
accounts.forEach(System.out::println);
}
(7)sorted()
/**
* Stream中间操作 7.sorted()
* 排序
*/
public static void StreamSorted(){
//将数组先转为Stream,再平方后再转为数组
int[] nums = {11, 2, 32, 14, 5};
int[] newNums = Arrays.stream(nums).sorted().toArray();
Arrays.stream(newNums).forEach(System.out::println);
//按对象的年龄排序
List<User> users = Arrays.asList(
new User(1,"灰太狼",new Date(),11,"男"),
new User(2,"喜羊羊",new Date(),10,"男"),
new User(3,"小灰灰",new Date(),15,"男")
);
List<User> newUsers = users.stream().sorted((p1, p2) -> {
return p1.getAge().compareTo(p2.getAge());
}).collect(Collectors.toList());
newUsers.forEach(System.out::println);
}
除此之外还有其他的中间操作,以后遇到接着补充…
最终操作
最终操作将结果转换成某种形式输出
(1)allMatch()
/**
* Stream终止操作 1.allMatch()
* 检查是否匹配所有元素
*/
public static void StreamAllMatch(){
//判断数组的元素是否都是在0-20之间
int[] nums = {11, 2, 32, 14, 5};
boolean isBetweenZeroTwenty = Arrays.stream(nums).allMatch(n -> (n >= 0) && (n <= 20));
System.out.println("数组的元素是否都是在0-20之间:"+isBetweenZeroTwenty);
//判断user列表的元素的年纪是否都是大于等于18岁
List<User> users = Arrays.asList(
new User(1,"灰太狼",new Date(),11,"男"),
new User(2,"喜羊羊",new Date(),10,"男"),
new User(3,"小灰灰",new Date(),15,"男")
);
boolean isAdult = users.stream().allMatch(u -> u.getAge() >= 18);
System.out.println("user列表的元素的年纪是否都是大于等于18岁:"+isAdult);
}
(2)max()和 min()
/**
* Stream终止操作 2.max()和 min()
* 查找最大值
*/
public static void StreamMaxMin(){
//返回数组最大值
int[] nums = {11, 2, 32, 14, 5};
int numMax = Arrays.stream(nums).max().getAsInt();
int numMin = Arrays.stream(nums).min().getAsInt();
System.out.println("数组最大值:"+numMax);
System.out.println("数组最小值:"+numMin);
//返回年纪最大值
List<User> users = Arrays.asList(
new User(1,"灰太狼",new Date(),11,"男"),
new User(2,"喜羊羊",new Date(),10,"男"),
new User(3,"小灰灰",new Date(),15,"男")
);
Optional<User> userMax = users.stream().max((p, q) -> {
return p.getAge().compareTo(q.getAge());
});
Optional<User> userMin = users.stream().min((p, q) -> {
return p.getAge().compareTo(q.getAge());
});
System.out.println("年纪最大的是 : "+userMax.get());
System.out.println("年纪最小的是 : "+userMin.get());
}
(3)sum()和 average()
/**
* Stream终止操作 3.sum()和 average()
* 查找最大值
*/
public static void StreamSumAve(){
//求和与平均值
int[] nums = {11, 2, 32, 14, 5};
int numSum = Arrays.stream(nums).sum();//求和
double numAve = Arrays.stream(nums).average().getAsDouble();//求平均值
System.out.println("求和 :"+numSum);
System.out.println("平均值:"+numAve);
//对象的求和与平均值
List<User> users = Arrays.asList(
new User(1,"灰太狼",new Date(),11,"男"),
new User(2,"喜羊羊",new Date(),10,"男"),
new User(3,"小灰灰",new Date(),15,"男")
);
Integer userAgeSum = users.stream().map(u -> u.getAge()).reduce(Integer::sum).get();
Double userAgeAve = users.stream().collect(Collectors.averagingInt(p -> p.getAge()));
System.out.println("用户年纪求和 : "+userAgeSum);
System.out.println("用户年纪平均值 : "+userAgeAve);
}
Stream高级应用
下面是Stream的一些高级应用,个人觉得这个十分重要,像统计数据方面,真的是方便。
(1)排序并分组
/**
* Stream高级应用 1.排序并分组
*
*/
public static void StreamGroupingOrderBy(){
try {
SimpleDateFormat simFormat = new SimpleDateFormat("yyyy/MM/dd");
//对象的求和与平均值
List<User> users = Arrays.asList(
new User(1,"灰太狼",simFormat.parse("2018/01/23"),11,"男"),
new User(2,"红太狼",simFormat.parse("2018/01/23"),10,"女"),
new User(2,"美羊羊",simFormat.parse("2001/12/03"),10,"女"),
new User(3,"小灰灰",simFormat.parse("2022/02/13"),15,"男"),
new User(3,"懒羊羊",simFormat.parse("2020/02/13"),15,"男")
);
//按性别分组
Map<String, List<User>> collectSex = users.stream()
.sorted(Comparator.comparing(User::getBirthday))
.collect(Collectors.groupingBy(User::getSex));
//输出
collectSex.forEach((k,v)->{
System.out.println("key : "+k+" value : "+v);
});
//按出生日期的day分组
Map<Date, List<User>> collectBirthday= users.stream()
.sorted(Comparator.comparing(User::getBirthday))
.collect(Collectors.groupingBy(u->u.getBirthday()));
//输出
collectBirthday.forEach((k,v)->{
System.out.println("key : "+simFormat.format(k)+" value : "+v);
});
} catch (ParseException e) {
e.printStackTrace();
}
}
(2)集合之间转换
/**
* Stream高级应用 2.集合之间转换
*
*/
public static void StreamConvert(){
try {
/**
* List ----> Map
*/
SimpleDateFormat simFormat = new SimpleDateFormat("yyyy/MM/dd");
List<User> users = Arrays.asList(
new User(1,"灰太狼",simFormat.parse("2018/01/23"),11,"男"),
new User(2,"红太狼",simFormat.parse("2018/01/23"),10,"女"),
new User(2,"美羊羊",simFormat.parse("2001/12/03"),10,"女"),
new User(3,"小灰灰",simFormat.parse("2022/02/13"),15,"男"),
new User(3,"懒羊羊",simFormat.parse("2020/02/13"),15,"男")
);
//以name为key,age为value,将list<User>转为Map
Map<String, Integer> map = users.stream().collect(Collectors.toMap(User::getName, User::getAge));
map.forEach((key,value)->{
System.out.println("key:"+key+" value:"+value);
});
/**
* List -----> Set
*/
Set<User> collect1 = users.stream().collect(Collectors.toSet());
collect1.forEach(System.out::println);
/**
* Map -----> List
*/
List<String> stringList = map.entrySet().stream().map(m -> m.getKey()).collect(Collectors.toList());
stringList.forEach(System.out::println);
/**
* Map ----> Set
*/
Set<String> stringSet = map.entrySet().stream().map(m -> m.getKey()).collect(Collectors.toSet());
stringSet.forEach(System.out::println);
/**
* Set ----> List
*/
List<String> list = stringSet.stream().collect(Collectors.toList());
list.forEach(System.out::println);
} catch (ParseException e) {
e.printStackTrace();
}
}
三、Optional
Optional作为一个容器类,主要用于尽量避免空指针异常
package lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* @ClassName : Optional
* @Description : java8新特性Optional使用方法
* @Author : CJH
* @Date: 2020-08-16 16:11
*/
public class TheOptional {
/*
* 一、Optional 容器类:用于尽量避免空指针异常
* Optional.of(T t) : 创建一个 Optinal 实例
* Optional.empty() : 创建一个空的 Optional 实例
* Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
* isPresent() : 判断是否包含值
* orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
* orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
* map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
* flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
*/
private String str1;
/**
* Optional 容器类:用于尽量避免空指针异常
* 三种创建Optional对象方式
*/
private static void createOptional(){
/**创建一个空的 Optional 实例**/
Optional.empty();
/**Optional.of(T t) : 创建一个 非null的Optional实例,如果是null则会报出空指针异常**/
Optional<String> op2 = Optional.of("张三");
op2.stream().forEach(System.out::println);
//Optional.of(null).stream().forEach(System.out::println);//抛出空指针异常
/**Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例**/
Optional<String> op3 = Optional.ofNullable("李四");
op3.stream().forEach(System.out::println);
//Optional.ofNullable(null).stream().forEach(System.out::println);
}
/**
* Optional容器类的使用
*/
private static void useOptional(){
/**
* 除了下面的这些方法外,还有map,filter等方法
* **/
Optional<String> op1 = Optional.ofNullable("Tony");
//当不为null时执行,为null时就不执行
op1.ifPresent(str1->{
System.out.println("str1="+str1);
});
Optional<String> op2 = Optional.ofNullable(null);
//当为null时赋默认值为”this is null“
op2.orElse("this is null");
op2.stream().forEach(System.out::println);
Optional<String> op3 = Optional.ofNullable(null);
//当为null时自定义返回值
op3.orElseGet(()->{
System.out.println("hello world");
return "hello world";
});
op3.stream().forEach(System.out::println);
Optional<String> op4= Optional.ofNullable(null);
//当为null时抛出自定义异常
op4.orElseThrow(()->{
throw new RuntimeException("运行时异常");
});
op4.stream().forEach(System.out::println);
}
/**
* 经常用于list或map等的判空
* @param list
*/
private static void oftenOptional(List<String> list){
Optional.ofNullable(list)
.map(List::stream)//如果不为null则构建相应的流
.orElseGet(Stream::empty)//如果为空则构建一个空的流
//.map()//进行相应处理
.forEach(System.out::println);//遍历
}
public static void main(String[] args) {
//createOptional();
//useOptional();
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
oftenOptional(list);
//oftenOptional(null);
}
}