Java8最大的特性就是提供了一组流式API对于集合的操作,那么我们具体来看看有那些个API是如何对集合进行操作的
构建集合
在将如何够一个集合之前,我们先来看看Stream这个接口,我们所有所使用的对于集合操作的API都定义在了这个接口里,这个类除了定义了一些接口之外,还定义了很多的静态方法,以便于我们能更好的操作,其中就包括构建集合
我们平时在写程序的时候,有时候往往需要自己来手动构建一个集合,那么对于集合的构建,我们可以这样来写
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
}
但是这样写虽然可以达到我们的目的,但是却花了五行代码,这个编码的过程非常的繁琐
Stream类就给我们提供了一个静态方法of,我们可以通过这个方法来构建一个集合
public static void main(String[] args) {
List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
System.out.println(list);
}
一行代码就能搞定
如果要对现有的集合使用流式API来操作,那这个集合必须式Stream流,我们只需要调用集合对象的stream方法就可以转换成Stream流了,比如我们需要对集合做过滤操作
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 调用集合对象的stream方法将集合转换成Stream流
Stream<String> stream = list.stream();
// 调用filter集合做过滤操作
List<String> a = stream.filter(ele -> ele.equals("a")).collect(Collectors.toList());
}
forEach遍历
forEach操作是对集合最为简单的操作了,假如我们需要把集合中每个值都打印出来,以前的方式是这样的
public static void main(String[] args) {
List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
for (String s : list) {
System.out.println(s);
}
}
使用forEach,一行代码就可以了
public static void main(String[] args) {
List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
list.forEach(System.out::println);
}
除了打印之外,比如我们还需要对集合里的元素做具体的操作话,可以这样写
public static void main(String[] args) {
list.forEach(element -> {
element.toUpperCase();
});
}
map映射
我们经常在写业务逻辑的时候,通常会把集合里的数据通过一系列的逻辑操作之后添加到另外的一个集合中,比如我们有一个集合,里面都是用户数据,我们需要收集这些用户的名称,以前的方式是这样的
public static void main(String[] args) {
List<User> userList = Stream.of(new User("zhangsan",23,"男"), new User("lisi",30,"男")).collect(Collectors.toList());
List<String> userNameList = new ArrayList<String>();
for (User user : userList) {
userNameList.add(user.getName());
}
System.out.println(userNameList);
}
数据从一个集合到另外的一个集合,这个过程我们成为映射,Stream提供了一个map方法,我们可以通过这个方法来完成这个映射的过程
public static void main(String[] args) {
List<String> userNameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());
}
flatMap映射
上面的map映射通常是对只有一次循环的操作,那如果我们需要获取每个用户下的订单数据的订单ID的话,那此时就需要两次循环了,除了循环每个用户之外还需要循环每个用户下的订单数据,我们如果用forEach循环的话是这样的
public static void main(String[] args) {
userList.forEach(user -> {
user.getOrderList().forEach(order -> orderIdList.add(order.getOrderId()));
});
}
Stream提供了一个flatMap方法,它可以将这两个循环合并到一起,我们可以这样来写
public static void main(String[] args) {
List<String> orderIdList = userList.stream().flatMap(user -> user.getOrderList().stream()).map(order -> order.getOrderId()).collect(Collectors.toList());
}
filter过滤
我们在集合操作的过程中通常也会有一些数据过滤的操作,比如我们如果需要把集合里用户名称为 lisi 的给过滤掉的话,我们可以用forEach这样操作
public static void main(String[] args) {
List<User> filterUserList = new ArrayList<User>();
userList.forEach(user -> {
if(!user.getName().equals("lisi")){
filterUserList.add(user);
}
});
}
Stream提供了一个filter方法,这个方里我们只需要将判断条件添加进去就可以了,我们可以这样来操作
public static void main(String[] args) {
List<User> filterUserList = userList.stream().filter(user -> !"lisi".equals(user.getName())).collect(Collectors.toList());
}
max和min操作
我们如果要获取集合中的最大值或最小值,Stream也给我们提供了max和min两个方法,我们如果要统计集合中做大年龄的用户的话,可以这样做
public static void main(String[] args) {
User user = userList.stream().max(Comparator.comparing(user -> user.getAge())).get();
}
max方法里还需要传入一个Comparator比较器,这个比较器提供了一个comparing静态方法,我们只需要把需要对比的参数传入进去就行了,因为max方法是返回的一个Optional对象,因此我们还需要调用这个对象的get方法才能获取到具体的值
min方法跟max方法的用法是一样的
public static void main(String[] args) {
User user = userList.stream().min(Comparator.comparing(user -> user.getAge())).get();
}
reduce操作
reduce方法是可以实现从一组值中生成一个值,比如上面的获取从一个用户集合中获取最大年龄的用户,我们上面用的max和min其实都是基于reduce方法来操作的,我们如果通过reduce方法来获取集合中最大年龄的用户的话,可以这样来操作
public static void main(String[] args) {
User user = userList.stream().reduce((acc, element) -> acc.getAge() > element.getAge() ? element : acc).get();
}
除了上面的操作之外,我们还可以通过这个方法来实现一个累加的操作,我们如果要把集合中元素进行累加,我们这样这样做
public static void main(String[] args) {
List<Integer> accountlist = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
int count = accountlist.stream().reduce(0,(acc,element) -> acc + element);
System.out.println(count);
}
这里reduce方法传入了两个参数,其中第一个参数标识默认以0为起点进行累加,acc是累加器,保存着当前的累加结果,element是当前元素
reduce 这个方法对于上面的两种场景在开发项目中很少用得到,以作者的经历来讲,对于 reduce 方法最为经典的场景就是用于字符串的拼接
假设我们有一个集合,需要对里面的元素拼接成一个字符串并以逗号的形式来分割,平常最为常用的方式是这样
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0 ; i < list.size() ; i++){
String s = list.get(i);
if(i == list.size() - 1){
stringBuilder.append(s);
break;
}
stringBuilder.append(s+",");
}
System.out.println(stringBuilder.toString());
}
我们如果用 reduce 方法来做拼接的操作就变得非常简单了
public static void main(String[] args) {
List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
String str = list.stream().reduce((acc, element) -> acc + "," + element).get();
System.out.println(str);
}
集合转换
我们在上面的例子中都会有一个collect方法,这个方法就是Stream提供的一个收集器,我们可以通过这个方法来将元素存放到不通的集合中,假如我们需要将存放用户数据的集合转换成一个Set集合,那么我们可以这样来做
public static void main(String[] args) {
Set<User> userSet = userList.stream().collect(Collectors.toSet());
}
如果要转换成Map集合的话,可以通过调用Collectors的toMap方法
public static void main(String[] args) {
Map<String, User> collect = userList.stream().collect(Collectors.toMap(User::getName, user -> user));
}
这里我们直接把用户的名称作为key,把user对象做为value
我们还可以自己定制化的创建集合来收集元素,可以调用Collectors的toCollection方法,我们如果要转换成TreeSet的话,可以这样
public static void main(String[] args) {
TreeSet<User> userTreeSet = userList.stream().collect(Collectors.toCollection(TreeSet::new));
}
集合的分组
我们如果要对集合中的用户数据进行分组,我们可以使用groupingBy,这个方法也是Collectors提供的一个静态方法,我们以用户的性别进行分组,可以这样来写
public static void main(String[] args) {
Map<String, List<User>> groupByUser = userList.stream().collect(Collectors.groupingBy(user -> user.getSex()));
}