Java8 List<对象> 转 Set、Map(高级)、排序、分组、统计
实体类
import lombok.Getter; import lombok.Setter; @Getter @Setter public class Student { private int id; private String name; private String score; private int classNo; public Student(int id, String name, String score, int classNo) { this.id = id; this.name = name; this.score = score; this.classNo = classNo; } }
Main
import com.demo.entity.Student; import java.util.*; import java.util.stream.Collectors; public class Main { private static List<String> simpleList = new ArrayList<>(); private static List<Student> normalList = new ArrayList<>(); static { simpleList.add("apple"); simpleList.add("apple"); simpleList.add("banana"); simpleList.add("orange"); normalList.add(new Student(1, "Emma", "A", 701)); normalList.add(new Student(2, "Larissa", "S", 701)); normalList.add(new Student(3, "Sophia", "B", 701)); normalList.add(new Student(4, "Ashley", "B", 702)); normalList.add(new Student(5, "May", "C", 702)); normalList.add(new Student(6, "Hailey", "D", 702)); normalList.add(new Student(7, "Kelly", "S", 703)); normalList.add(new Student(8, "Amy", "A", 703)); normalList.add(new Student(9, "Wesley", "C", 703)); } public static void main(String[] args){ // TODO } }
List<String> 转 Set<String>
System.out.println("----------------简单List---------------"); simpleList.forEach(System.out::println); System.out.println("----------------简单List转Set---------------"); Set<String> simpleSet = new HashSet<>(simpleList); simpleSet.forEach(System.out::println);
输出:
----------------简单List--------------- apple apple banana orange ----------------简单List转Set--------------- banana orange apple
List<Student> 转 Set<Integer>
System.out.println("----------------普通List---------------"); normalList.forEach(System.out::println); System.out.println("----------------普通List转Set---------------"); Set<Integer> normalSet = normalList.stream().map(Student::getClassNo).collect(Collectors.toSet()); normalSet.forEach(System.out::println);
输出:
----------------普通List--------------- Student{id=1, name='Emma', score='A', classNo=701} Student{id=2, name='Larissa', score='S', classNo=701} Student{id=3, name='Sophia', score='B', classNo=701} Student{id=4, name='Ashley', score='B', classNo=702} Student{id=5, name='May', score='C', classNo=702} Student{id=6, name='Hailey', score='D', classNo=702} Student{id=7, name='Kelly', score='S', classNo=703} Student{id=8, name='Amy', score='A', classNo=703} Student{id=9, name='Wesley', score='C', classNo=703} ----------------普通List转Set--------------- 701 702 703
List<Student> 转 List<String>
System.out.println("----------------普通List转List---------------"); List<String> list = normalList.stream().map(Student::getName).collect(Collectors.toList()); list.forEach(System.out::println);
输出:
----------------普通List转List---------------
Emma
Larissa
Sophia
Ashley
May
Hailey
Kelly
Amy
Wesley
List<Student> 转 Map<Integer,Student>
System.out.println("----------------普通List转Map---------------"); Map<Integer,Student> normalMap = normalList.stream().collect(Collectors.toMap(Student::getId,(b)->b)); normalMap.forEach((id, student) -> { System.out.println(id + "::" + student); });
输出:
----------------普通List转Map--------------- 1::Student{id=1, name='Emma', score='A', classNo=701} 2::Student{id=2, name='Larissa', score='S', classNo=701} 3::Student{id=3, name='Sophia', score='B', classNo=701} 4::Student{id=4, name='Ashley', score='B', classNo=702} 5::Student{id=5, name='May', score='C', classNo=702} 6::Student{id=6, name='Hailey', score='D', classNo=702} 7::Student{id=7, name='Kelly', score='S', classNo=703} 8::Student{id=8, name='Amy', score='A', classNo=703} 9::Student{id=9, name='Wesley', score='C', classNo=703}
复杂一点的转换:(List转Map处理重复key)
List<Student> students = new ArrayList<>(normalList); System.out.println("----------------原数据---------------"); students.forEach(System.out::println); System.out.println("----------------List<Student>转Map<String, Student>重复key只保留前者---------------"); // 重复key处理 (s1, s2) -> s1) Map<Integer, Student> classStudentMap = students.stream().collect(Collectors.toMap(Student::getClassNo, s -> s, (s1, s2) -> s1)); classStudentMap.forEach((classNo, student) -> System.out.println(classNo + "::" + student)); System.out.println("----------------List<Student>转Map<String, List<Student>>---------------"); // 重复key处理成一个集合 Map<Integer, List<Student>> listMap = students.stream().collect(Collectors.toMap(Student::getClassNo, s -> { List<Student> l = new ArrayList<>(); l.add(s); return l; }, (List<Student> s1, List<Student> s2) -> { s1.addAll(s2); return s1; })); listMap.forEach((learn, student) -> System.out.println(learn + "::" + student));
输出:
----------------原数据--------------- Student{id=1, name='Emma', score='A', classNo=701} Student{id=2, name='Larissa', score='S', classNo=701} Student{id=3, name='Sophia', score='B', classNo=701} Student{id=4, name='Ashley', score='B', classNo=702} Student{id=5, name='May', score='C', classNo=702} Student{id=6, name='Hailey', score='D', classNo=702} Student{id=7, name='Kelly', score='S', classNo=703} Student{id=8, name='Amy', score='A', classNo=703} Student{id=9, name='Wesley', score='C', classNo=703} ----------------List<Student>转Map<String, Student>重复key只保留前者--------------- 701::Student{id=1, name='Emma', score='A', classNo=701} 702::Student{id=4, name='Ashley', score='B', classNo=702} 703::Student{id=7, name='Kelly', score='S', classNo=703} ----------------List<Student>转Map<String, List<Student>>--------------- 701::[Student{id=1, name='Emma', score='A', classNo=701}, Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=3, name='Sophia', score='B', classNo=701}] 702::[Student{id=4, name='Ashley', score='B', classNo=702}, Student{id=5, name='May', score='C', classNo=702}, Student{id=6, name='Hailey', score='D', classNo=702}] 703::[Student{id=7, name='Kelly', score='S', classNo=703}, Student{id=8, name='Amy', score='A', classNo=703}, Student{id=9, name='Wesley', score='C', classNo=703}]
Map<String, List<Student>>转List<Student> 和 Map<String, List<Student>>转List<List<Student>>
List<Student> students = new ArrayList<>(normalList); // 先分组,准备好数据 Map<String, List<Student>> grouping = students.stream().collect(Collectors.groupingBy(Student::getScore)); System.out.println("----------------Map<String, List<Student>>转List<Student>---------------"); // 把map的values全部拆出来 List<Student> collect = grouping.entrySet().stream() .flatMap(map -> map.getValue().stream()) .collect(Collectors.toList()); collect.forEach(System.out::println); System.out.println("----------------Map<String, List<Student>>转List<List<Student>>---------------"); // 只要map的value,但是不改变格式 grouping.values().forEach(System.out::println);
输出:
----------------Map<String, List<Student>>转List<Student>--------------- Student{id=1, name='Emma', score='A', classNo=701} Student{id=8, name='Amy', score='A', classNo=703} Student{id=3, name='Sophia', score='B', classNo=701} Student{id=4, name='Ashley', score='B', classNo=702} Student{id=5, name='May', score='C', classNo=702} Student{id=9, name='Wesley', score='C', classNo=703} Student{id=2, name='Larissa', score='S', classNo=701} Student{id=7, name='Kelly', score='S', classNo=703} Student{id=6, name='Hailey', score='D', classNo=702} ----------------Map<String, List<Student>>转List<List<Student>>--------------- [Student{id=1, name='Emma', score='A', classNo=701}, Student{id=8, name='Amy', score='A', classNo=703}] [Student{id=3, name='Sophia', score='B', classNo=701}, Student{id=4, name='Ashley', score='B', classNo=702}] [Student{id=5, name='May', score='C', classNo=702}, Student{id=9, name='Wesley', score='C', classNo=703}] [Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=7, name='Kelly', score='S', classNo=703}] [Student{id=6, name='Hailey', score='D', classNo=702}]
分组
List<Student> students = new ArrayList<>(normalList); System.out.println("----------------分组---------------"); // 根据key分组 Map<String, List<Student>> grouping = students.stream().collect(Collectors.groupingBy(Student::getScore)); grouping.forEach((score, student) -> System.out.println(score + "::" + student)); System.out.println("----------------按照多个属性分组---------------"); // 根据多个key的组合分组 grouping = students.stream().collect(Collectors.groupingBy( e -> e.getClassNo() + "_" + e.getScore())); grouping.forEach((learn, student) -> System.out.println(learn + "::" + student));
输出:
----------------分组--------------- A::[Student{id=1, name='Emma', score='A', classNo=701}, Student{id=8, name='Amy', score='A', classNo=703}] B::[Student{id=3, name='Sophia', score='B', classNo=701}, Student{id=4, name='Ashley', score='B', classNo=702}] C::[Student{id=5, name='May', score='C', classNo=702}, Student{id=9, name='Wesley', score='C', classNo=703}] S::[Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=7, name='Kelly', score='S', classNo=703}] D::[Student{id=6, name='Hailey', score='D', classNo=702}] ----------------按照多个属性分组--------------- 702_C::[Student{id=5, name='May', score='C', classNo=702}] 703_A::[Student{id=8, name='Amy', score='A', classNo=703}] 702_B::[Student{id=4, name='Ashley', score='B', classNo=702}] 701_S::[Student{id=2, name='Larissa', score='S', classNo=701}] 703_C::[Student{id=9, name='Wesley', score='C', classNo=703}] 703_S::[Student{id=7, name='Kelly', score='S', classNo=703}] 702_D::[Student{id=6, name='Hailey', score='D', classNo=702}] 701_B::[Student{id=3, name='Sophia', score='B', classNo=701}] 701_A::[Student{id=1, name='Emma', score='A', classNo=701}]
排序
List<Student>根据名字排序
System.out.println("----------------List排序---------------"); // 虽然这里是浅拷贝,但是只影响修改而不影响排序 List<Student> students = new ArrayList<>(normalList); Collections.sort(students, Comparator.comparing(Student::getName)); // 比上面更简洁 // students.sort(Comparator.comparing(Student::getName)); students.forEach(System.out::println);
输出:
----------------List排序--------------- Student{id=8, name='Amy', score='A', classNo=703} Student{id=4, name='Ashley', score='B', classNo=702} Student{id=1, name='Emma', score='A', classNo=701} Student{id=6, name='Hailey', score='D', classNo=702} Student{id=7, name='Kelly', score='S', classNo=703} Student{id=2, name='Larissa', score='S', classNo=701} Student{id=5, name='May', score='C', classNo=702} Student{id=3, name='Sophia', score='B', classNo=701} Student{id=9, name='Wesley', score='C', classNo=703}
List<String>排序
System.out.println("----------------简单List排序---------------"); List<String> list = new ArrayList<>(simpleList); System.out.println("----------------正序---------------"); list.sort((a,b) -> a.compareTo(b)); // 更简洁的方式 // list.sort(Comparator.naturalOrder()); list.forEach(System.out::println); System.out.println("----------------倒序---------------"); list.sort(Comparator.reverseOrder()); list.forEach(System.out::println);
输出:
----------------简单List排序--------------- ----------------正序--------------- apple apple banana orange ----------------倒序--------------- orange banana apple apple
Map排序
List<Student> students = new ArrayList<>(normalList); // 先按照成绩分组,准备好数据 Map<String, List<Student>> grouping = students.stream().collect(Collectors.groupingBy(Student::getScore)); System.out.println("----------------Map<String, List<Student>>排序---------------"); Map<String, List<Student>> result = new LinkedHashMap<>(); // Map的key有特殊处理 grouping.entrySet().stream() .sorted((o1,o2) -> { Integer k1 = getWeight(o1.getKey()); Integer k2 = getWeight(o2.getKey()); return k1.compareTo(k2); }) .forEachOrdered(x -> result.put(x.getKey(), x.getValue())); result.forEach((learn, student) -> System.out.println(learn + "::" + student)); System.out.println("----------------"); Map<String, List<Student>> result2 = new LinkedHashMap<>(); // 仅仅按照key排序 grouping.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEachOrdered(x -> result2.put(x.getKey(), x.getValue())); result2.forEach((learn, student) -> System.out.println(learn + "::" + student)); System.out.println("----------------"); Map<String, List<Student>> result3 = new LinkedHashMap<>(); // 等价第一个,只是省去了getKey方法 grouping.entrySet().stream() .sorted(Map.Entry.comparingByKey((o1,o2) -> { Integer k1 = getWeight(o1); Integer k2 = getWeight(o2); return k1.compareTo(k2); })) .forEachOrdered(x -> result3.put(x.getKey(), x.getValue())); result3.forEach((learn, student) -> System.out.println(learn + "::" + student));
权重方法:
/** * 不同成绩有不同的排序权重 * @param score * @return */ public static Integer getWeight(String score){ switch (score){ case "S": return 1; case "A": return 2; case "B": return 3; case "C": return 2; case "D": return 2; default:return 0; } }
输出:
----------------Map<String, List<Student>>排序--------------- S::[Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=7, name='Kelly', score='S', classNo=703}] A::[Student{id=1, name='Emma', score='A', classNo=701}, Student{id=8, name='Amy', score='A', classNo=703}] C::[Student{id=5, name='May', score='C', classNo=702}, Student{id=9, name='Wesley', score='C', classNo=703}] D::[Student{id=6, name='Hailey', score='D', classNo=702}] B::[Student{id=3, name='Sophia', score='B', classNo=701}, Student{id=4, name='Ashley', score='B', classNo=702}] ---------------- A::[Student{id=1, name='Emma', score='A', classNo=701}, Student{id=8, name='Amy', score='A', classNo=703}] B::[Student{id=3, name='Sophia', score='B', classNo=701}, Student{id=4, name='Ashley', score='B', classNo=702}] C::[Student{id=5, name='May', score='C', classNo=702}, Student{id=9, name='Wesley', score='C', classNo=703}] D::[Student{id=6, name='Hailey', score='D', classNo=702}] S::[Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=7, name='Kelly', score='S', classNo=703}] ---------------- S::[Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=7, name='Kelly', score='S', classNo=703}] A::[Student{id=1, name='Emma', score='A', classNo=701}, Student{id=8, name='Amy', score='A', classNo=703}] C::[Student{id=5, name='May', score='C', classNo=702}, Student{id=9, name='Wesley', score='C', classNo=703}] D::[Student{id=6, name='Hailey', score='D', classNo=702}] B::[Student{id=3, name='Sophia', score='B', classNo=701}, Student{id=4, name='Ashley', score='B', classNo=702}]
如果你要倒序的话
System.out.println("----------------倒序----------------"); Map<String, List<Student>> result4 = new LinkedHashMap<>(); // 仅仅按照key排序 grouping.entrySet().stream() .sorted(Map.Entry.<String, List<Student>>comparingByKey().reversed()) .forEachOrdered(x -> result4.put(x.getKey(), x.getValue())); result4.forEach((learn, student) -> System.out.println(learn + "::" + student)); System.out.println("----------------"); Map<String, List<Student>> result5 = new LinkedHashMap<>(); // 等价第一个,只是省去了getKey方法 grouping.entrySet().stream() .sorted(Map.Entry.<String, List<Student>>comparingByKey((o1,o2) -> { Integer k1 = getWeight(o1); Integer k2 = getWeight(o2); return k1.compareTo(k2); }).reversed()) .forEachOrdered(x -> result5.put(x.getKey(), x.getValue())); result5.forEach((learn, student) -> System.out.println(learn + "::" + student));
输出:
----------------倒序---------------- S::[Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=7, name='Kelly', score='S', classNo=703}] D::[Student{id=6, name='Hailey', score='D', classNo=702}] C::[Student{id=5, name='May', score='C', classNo=702}, Student{id=9, name='Wesley', score='C', classNo=703}] B::[Student{id=3, name='Sophia', score='B', classNo=701}, Student{id=4, name='Ashley', score='B', classNo=702}] A::[Student{id=1, name='Emma', score='A', classNo=701}, Student{id=8, name='Amy', score='A', classNo=703}] ---------------- B::[Student{id=3, name='Sophia', score='B', classNo=701}, Student{id=4, name='Ashley', score='B', classNo=702}] A::[Student{id=1, name='Emma', score='A', classNo=701}, Student{id=8, name='Amy', score='A', classNo=703}] C::[Student{id=5, name='May', score='C', classNo=702}, Student{id=9, name='Wesley', score='C', classNo=703}] D::[Student{id=6, name='Hailey', score='D', classNo=702}] S::[Student{id=2, name='Larissa', score='S', classNo=701}, Student{id=7, name='Kelly', score='S', classNo=703}]
统计
String s = "15, 11, 01, 03, 07, 12, 15, 12, 15, 02, 07, 14, 01, 03, 04, 05, 09, 04, 06, 12, 04, 07, 13, 10, 04, 14, 13, 11, 10, 16, 16, 04, 15, 03, 16, 08, 10, 05, 08, 11, 16, 04, 13, 07, 14, 06, 14, 10, 15, 02, 09, 16, 08, 11, 10, 01, 16, 12, 06, 01, 12, 01, 16, 12, 10, 04"; List<Integer> list = new ArrayList<>(); for(String i : s.split(", ")){ list.add(Integer.valueOf(i)); } // 平均值 System.out.println(list.stream().mapToDouble(value -> value).average()); // 总值 System.out.println(list.stream().mapToDouble(value -> value).sum()); // 数据数量 System.out.println(list.stream().mapToDouble(value -> value).count()); // 最大 System.out.println(list.stream().mapToDouble(value -> value).max()); // 最小 System.out.println(list.stream().mapToDouble(value -> value).min()); // 去重后计算总值 System.out.println(list.stream().mapToDouble(value -> value).distinct().sum());
也有其他数据类型的方法
结果:
OptionalDouble[9.121212121212121] 602.0 66 OptionalDouble[16.0] OptionalDouble[1.0] 136.0
=========================================分割线=================================================
Lambda表达式
格式:(params) -> {expression}
比如我们实现一个Runnable接口
Runnable run = new Runnable() {
@Override
public void run() {
// TODO
}
};
使用Lambda表达式
Runnable run = () -> {
// TODO
};
而可以使用Lambda的接口也是有限制的,即只能有一个方法。
函数式接口
规范:
1. 接口中只能有一个抽象方法
2. (可选)在接口上添加 @FunctionalInterface 注解,这样可以检测它是否是一个函数式接口。
比如:
@FunctionalInterface
public interface Fun{
void fun();
}
// 也可以使用泛型
@FunctionalInterface
public interface Fun<T>{
void fun(T t);
}
使用的时候
Fun fun = () -> {};
为什么Lambda引用外部变量需要final修饰?
1. 每个方法执行的时候都会在栈上创建一个栈帧,局部变量存放在栈帧中的局部变量表中,栈上的内容是线程私有的。
2. Lambda表达式是个匿名函数,准确来说是个匿名内部类接口的实现方法。而执行此代码的线程会在执行的时候单独创建一个栈帧,局部变量依然是私有的。
3. 由此可见,表达式里的current变量只是外部current变量的一个副本。而为了保证程序的正确性则强制要求这个被引用的局部变量被定义成final。
也就是说,人家也可以不加入这个检查,允许你改变外部局部变量的值,但是,程序运行的结果并不会如你所愿,所以,人家索性就打消你这个念头。
其它Java8新特性
1. computeIfAbsent
Map<String, String> map = new HashMap<>(); String key = "A"; String v = map.get(key); if (v == null){ System.out.println("处理key不存在的情况"); }
Java8可以这样处理
Map<String, String> map = new HashMap<>(); String key = "A"; String computeIfAbsent = map.computeIfAbsent(key, s -> { return "如果key不存在,则返回这个"; }); System.out.println(computeIfAbsent);
2. filter
过滤出特定数据,并取出特定字段
import lombok.Getter; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) throws Exception { List<People> list = new ArrayList<>(); list.add(new People(1, "男")); list.add(new People(2, "女")); list.add(new People(3, "女")); List<Integer> collect = list.stream().filter(i -> "女".equals(i.getSex())).map(People::getId).collect(Collectors.toList()); System.out.println(collect); } } @Getter class People{ private int id; private String sex; public People(int id, String sex) { this.id = id; this.sex = sex; } }
输出
对象里的BigDecimal累加
BigDecimal actualReceive = orderItems.stream().map(ItemDTO::getNumber).reduce(BigDecimal.ZERO, BigDecimal::add);
list分组累计
// 方式一 Map<String, Integer> sumMap = scoreList.stream().collect(Collectors.groupingBy(Student::getName, Collectors.summingInt(Student::getScore))); // 方式二 Map<String, Integer> sumMap = scoreList.stream().collect(Collectors.toMap(Student::getName, Student::getScore, Integer::sum));
Map根据value排序
Map<String, Integer> sumMap = new HashMap<>(); // 倒序 List<Map.Entry<String, Integer>> list = sumMap.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).collect(Collectors.toList()); // 正序 List<Map.Entry<String, Integer>> list2 = sumMap.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toList());
取对象list里,最小的日期
Date date = orderList.stream().map(Order::getCreateDate).min(Date::compareTo).orElse(null);
对象list分组之后,取单个字段为map的value
Map<String, List<String>> map = userList.stream().collect(Collectors.groupingBy(User::getAge, Collectors.mapping(User::getName, Collectors.toList())));
给List分批处理
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.0.0-android</version> </dependency>
引入依赖后
public static void main(String[] args) { List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25); for (List<Integer> part : Lists.partition(list, 10)) { System.out.println(part); } }
随时补充