import java.text.Collator;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
/**
* 声明一个类时,要考虑是否要重写toString()、equals()、hashCode()、
* 还有排序时要用的:是否要实现Comparable接口,若没有实现,是否要使用Comparator
* 特别是涉及的集合的时候
*/
public class Person {
public Person() {
}
public Person(String name, int age, Gender gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
private String name;
private int age;
private Gender gender;
//枚举性别
public enum Gender {
FEMALE("女"), MALE("男");
private String value;
Gender(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Gender getGender() {
return gender;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name) &&
gender == person.gender;
}
@Override
public int hashCode() {
return Objects.hash(name, age, gender);
}
@Override
public String toString() {
return String.format("Person[name=%s,age=%d,gender=%s]%n",name,age,gender==null?"未知":gender.getValue());
}
public static void main(String[] args) {
// System.out.println(Runtime.getRuntime().availableProcessors());
List<Person> list=new ArrayList<>();
list.add(new Person("张三",18,Person.Gender.MALE));
list.add(new Person("张三",18,Person.Gender.MALE));
list.add(new Person("李四",18,Person.Gender.MALE));
list.add(new Person("赵丽",18,Person.Gender.FEMALE));
list.add(new Person("钱婷",19,Person.Gender.FEMALE));
list.add(new Person("田七",25, Gender.MALE));
list.add(new Person("啊王八",25, Gender.FEMALE));
//demo(list);
//chineseDemo(list);
//collectors_collectingAndThen_demo(list);
//collectors_partitioningBy_demo(list);
//collectors_reducing_demo(list);
//collectors_summary_statistics_demo(list);
collectors_to_collection_demo(list);
}
/**
*将流变成集合的几种写法
* @param list
*/
private static void collectors_to_collection_demo(List<Person> list) {
List<Person> personList = list.stream().collect(Collectors.toList());
System.out.printf("返回的集合类型是:%s,该集合的元素有%s%n",personList.getClass(),personList);
Set<Person> personSet = list.stream().collect(Collectors.toSet());
System.out.printf("返回的集合类型是:%s,该集合的元素有%s%n",personSet.getClass(),personSet);
//如果有重复对象,对list进行list.stream().distinct()也还是会抛异常,因为distinct()是中间操作
// return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
// Map<Integer, String> integerStringMap = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName));
//System.out.printf("返回的集合类型是:%s,该集合的元素有%s%n",integerStringMap.getClass(),integerStringMap);
//如果不想抛异常,改成以下写法,因为默认key一样时,不是覆盖而是抛异常
TreeMap<Integer, String> treeMap = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName, (oldValue, newValue) -> newValue, TreeMap::new));
System.out.printf("返回的集合类型是:%s,该集合的元素有%s%n",treeMap.getClass(),treeMap);
//元素要实现comparable才能转换成功,不然就要在构造函数里传入一个Comparator
//TreeSet<Person> treeSet = list.stream().collect(Collectors.toCollection(TreeSet::new));
//System.out.printf("==返回的集合类型是:%s,该集合的元素有%s%n",treeSet.getClass(),treeSet);
//改进写法:
TreeSet<Person> treeSet = list.stream().collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getName))));
System.out.printf("==返回的集合类型是:%s,该集合的元素有%s%n",treeSet.getClass(),treeSet);
}
/**
* 对数据进行统计
* 其实是将求和、平均值、最值封装到一个统计对象里面了
* @param list
*/
private static void collectors_summary_statistics_demo(List<Person> list) {
IntSummaryStatistics intSummaryStatistics = list.stream().collect(Collectors.summarizingInt(Person::getAge));
System.out.println(intSummaryStatistics.getMax());
System.out.println(intSummaryStatistics.getMin());
System.out.println(intSummaryStatistics.getAverage());
System.out.println(intSummaryStatistics.getSum());
System.out.println(intSummaryStatistics.getCount());
}
/**
* 对数据进行聚合【规约、缩减、约简】
* 其实求和、平均值、最值问题也可以用reducing
* @param list
*/
private static void collectors_reducing_demo(List<Person> list) {
//要在流上执行简单的约简,可以使用stream.reduce(BinaryOperator)
//根据年龄进行分组,分组后获得每一组的最大年龄的人
//写法一:使用reducing
Map<Integer, Optional<Person>> map = list.stream()
.collect(Collectors.groupingBy(Person::getAge, Collectors.reducing(
BinaryOperator.maxBy(Comparator.comparing(Person::getAge)))));
System.out.println(map);
//写法二: 使用Collectors.maxBy,底层是调用写法一的写法
Map<Integer, Optional<Person>> resultMap = list.stream()
.collect(Collectors.groupingBy(Person::getAge, Collectors.maxBy( Comparator.comparing(Person::getAge))));
System.out.println(resultMap);
//其实求和、平均值、最值问题也可以用reducing,就像这样
Map<Gender, Integer> result = list.stream().collect(Collectors.groupingBy(Person::getGender,
Collectors.reducing(0, Person::getAge, Integer::sum)));//从0开始累加
System.out.println(result);
}
/**
* 对数据进行求平均值、求和,最大值、最小值
* @param list
*/
private static void collectors_averaging_demo(List<Person> list) {
//求年龄的平均值Collectors.averagingDouble(), Collectors.averagingLong()
Double ageAveraging = list.stream().collect(Collectors.averagingInt(Person::getAge));
System.out.println(ageAveraging.doubleValue());
//求年龄之和Collectors.summingDouble(),Collectors.summingLong()
Integer sum = list.stream().collect(Collectors.summingInt(Person::getAge));
System.out.println(sum);
//最大年龄的人
Optional<Person> maxAgePerson = list.stream().collect(Collectors.maxBy(Comparator.comparingInt(Person::getAge)));
System.out.printf("最大年龄的人:%s%n",maxAgePerson.orElseGet(()->new Person("",-1,null)));
//最小年龄的人
Optional<Person> minAgePerson = list.stream().collect(Collectors.minBy(Comparator.comparingInt(Person::getAge)));
System.out.printf("最小年龄的人:%s%n",minAgePerson.orElseGet(()->new Person("",-1,null)));
//问题来了,如果最大年龄的人有多个呢?-- 可以这么做
List<Person> personList = list.stream().collect(Collectors.groupingBy(Person::getAge, TreeMap::new, Collectors.toList()))
.lastEntry().getValue();//使用TreeMap的lastEntry()方法
System.out.printf("最大年龄的人:%s%n",personList);
}
/**
* 对数据进行分区
* @param list
*/
private static void collectors_partitioningBy_demo(List<Person> list) {
//对数据进行分区
Map<Boolean, List<Person>> booleanListMap = list.stream()
.collect(Collectors.partitioningBy(x -> x.getGender() == Gender.MALE));
System.out.println(booleanListMap);
对数据进行分区--后计算每个分区的数量
Map<Boolean, Long> countPartitionMap = list.stream().collect(Collectors.partitioningBy(x -> x.getGender() == Gender.MALE, Collectors.counting()));
System.out.println(countPartitionMap);
}
/**
* 对数据进行分组--分组完后对每一个list进行排序
* @param list
*/
private static void collectors_collectingAndThen_demo(List<Person> list) {
//收集完后返回一个list集合,对list集合进行按年龄【倒序】排序
/* List<Person> personList = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), resultList -> {
resultList.sort(Comparator.comparing(Person::getAge).reversed());
return resultList;
}));
System.out.println(personList);*/
//分组完后对每一个list进行排序--默认使用的Map是HashMap--return groupingBy(classifier, HashMap::new, downstream);
Map<Integer, List<Person>> listMap = list.stream().collect(Collectors.groupingBy(Person::getAge
, Collectors.collectingAndThen(Collectors.toList(), resultList -> {
resultList.sort(Comparator.comparing(Person::getName));
return resultList;
})));
System.out.println(listMap);
//使用TreeMap 来存储key
TreeMap<Integer, List<String>> listTreeMap = list.stream().collect(Collectors.groupingBy(Person::getAge//根据年龄分组
, TreeMap::new使用TreeMap 来存储key
, Collectors.mapping(Person::getName, Collectors.toList())));//将集合中的每个元素映射成姓名
System.out.println(listTreeMap);
}
/**
* 根据中文拼音姓名排序
* @param list
*/
private static void chineseDemo(List<Person> list) {
//按照姓名自然排序,姓名相同按年龄排序
list.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
//根据中文拼音姓名排序:写法一
list.sort((p1, p2) -> Collator.getInstance(Locale.CHINA).compare(p1.getName(), p2.getName()));
//根据中文拼音姓名排序:写法二
//告诉程序,我要根据姓名排序,不使用默认比较器,使用我指定比较器去排序
list.sort(Comparator.comparing(Person::getName, Collator.getInstance(Locale.CHINA)));
list.forEach(System.out::println);
}
private static void demo(List<Person> list) {
//根据中文姓名排序
list.sort((p1,p2)-> Collator.getInstance(Locale.CHINA).compare(p1.getName(),p2.getName()));
list.sort(Comparator.comparing(Person::getName,Collator.getInstance(Locale.CHINA)));
//重写了toString之后,for循环够足够优雅只需要一行
//因为打印方法的本质就是调用对象的toString()方法
list.forEach(System.out::println);
//注意:contains 中的对象要重写equals方法和hashCode方法
//因为其源代码就是调用对象的equals方法
//我们指定相同姓名相同年龄相同性别为同一对象
System.out.println( list.contains(new Person("张三",18, Gender.MALE)));
//去重复方式一:
//底层是通过LinkedHashSet去重的,而LinkedHashSet是会按照原先的顺序进行迭代的
List<Person> collect1 = list.stream().distinct().collect(Collectors.toList());
System.out.println(collect1);
//去重复方式二:
//返回的是一个HashSet 无序
Set<Person> collect = list.stream().collect(Collectors.toSet());
System.out.println(collect);
//根据年龄分组
//Function<Person, Integer> getAge = Person::getAge;
//Collector<Person, ?, Map<Integer, List<Person>>> personMapCollector = Collectors.groupingBy(Person::getAge);
Map<Integer, List<Person>> listMap = list.stream().collect(Collectors.groupingBy(Person::getAge));
System.out.println(listMap);
//根据年龄分组,并且只保留人名
Map<Integer, List<String>> listStringMap = list.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.mapping(Person::getName, Collectors.toList())));
//给定一个对象,如果该对象在list中存在,则删除
//jdk1.8以后只需要一行,removeIf方法结合了contains方法和remove方法
list.removeIf(e->e.equals(new Person("张三",18, Gender.MALE)));
System.out.println(list);
//list.indexOf()
//list.lastIndexOf()
//一个典型的流聚合操作就是filter-map-forEach,能否很方便地使用lambda表达式和方法引用
list.stream().filter(p->p.getAge()==18&&p.getGender()== Gender.MALE).
map(Person::getName).forEachOrdered(System.out::println);
//reduction operation 减少操作(聚合操作)
//sum方法其实就是调用reduce(0, Integer::sum)方法
//reduce(0, Integer::sum)的第一个参数是初始值和返回结果,第一个参数会累加第二个参数的返回值
//以下三条式子等同
Integer totalAge = list.stream() .mapToInt(Person::getAge).sum();
Integer reduce = list.stream().map(Person::getAge).reduce(0, (a, b) -> a + b);
Integer reduce2 = list.stream().map(Person::getAge).reduce(0, Integer::sum);
//lambda表达式不能访问非final的局部变量
//检索每个性别的平均年龄
Map<Gender, Double> collect2 = list.stream().collect(Collectors.groupingBy(Person::getGender, Collectors.averagingInt(Person::getAge)));
}
}