Stream API
Stream API 三部曲
1. 创建流
/*
* 1. 创建Stream的方式
*/
// collection.stream()
Stream<Employee> stream1 = employees.stream();
// Arrays.stream(T[] array)
Stream<Employee> stream2 = Arrays.stream(empArr);
// Stream.of(T[] arr)
Stream<Employee> empArr = Stream.of(this.empArr);
// Stream.iterate(seed,UnaryOperator<>)
Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);
// Stream.generate(Supplier<>)
Stream<Double> stream5 = Stream.generate(() -> Math.random());
2. 中间操作
2.0 一次加载,惰性求值
2.1 筛选
/**
* 中间操作
* 筛选
* 筛选年纪大于35岁的人员
* 内部迭代输出
*/
@Test
public void test01() {
employees.stream()
.filter(e -> {
System.out.println("中间操作:" + e.getName() + "是" + e.getAge() + "岁");
return e.getAge() > 35;
})
.forEach(System.out::println);
}
2.2 切片
2.2.1 limit
limit(Long num)—— 截断前num个元素,之后不会向后迭代
/**
* 切片操作
* 只取前两个
*/
@Test
public void test05() {
employees.stream()
.limit(2)
.forEach(System.out::println);
}
limit切片会导致短路,也即是不会继续向后迭代:
/**
* 切片操作
* limit会造成短路
*/
@Test
public void test05_2() {
employees.stream()
.filter(employee -> {
System.out.println("短路");
return employee.getSalary()>3000;
})
.limit(2)
.forEach(System.out::println);
}
2.2.2 skip
Skip(Long num) —— 跳过前num个元素
/**
* 切片操作
* 跳过前两个
*/
@Test
public void test06() {
employees.stream()
.skip(2)
.forEach(System.out::println);
}
2.3 排序
2.3.1 自然排序
Stream<T> sorted();
/**
* 自然排序
*/
@Test
public void test03_1(){
int[] arr = {1,5,9,3,2,7,6,9};
Arrays.stream(arr).sorted().forEach(System.out::println);
}
2.3.2 定制排序
复杂的对象需要定制排序规则
Stream<T> sorted(Comparator<? super T> comparator);
/**
* 排序操作
* 按照工资正序排序
*/
@Test
public void test03() {
employees.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.forEach(System.out::println);
}
2.4 去重
2.4.1 默认去重
Stream<T> distinct();
/**
* 去重操作
* 通过流生成的hashCode和equals方法去重
* 若是对象没有重写hashcode和equals方法,将无法成功去重
*/
@Test
public void test04() {
employees.stream()
.distinct()
.forEach(System.out::println);
}
2.4.4 定制去重
参考: https://blog.csdn.net/haiyoung/article/details/80934467
https://www.concretepage.com/java/jdk-8/java-8-distinct-example
a 重写Employee的hashCode 和 equals方法
因为sorted()是基于 hashCode() 和 equals() 方法进行工作,所以第一个方法是重写这两个方法。
b 借助filter过滤
Stream<T> filter(Predicate<? super T> predicate);
static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> employeeMap = new ConcurrentHashMap<>();
return e -> employeeMap.putIfAbsent(keyExtractor.apply(e), Boolean.TRUE) == null;
}
/**
* 对象去重
* 按照指定的方式去重
*/
@Test
public void test04_1() {
employees.stream()
.filter(distinctByKey(employee -> employee.getId()))
.forEach(System.out::println);
}
2.5 映射
2.5.1 map映射
对流中每一个元素进行操作
/**
* map
* 对每一个元素进行操作
*/
@Test
public void test01(){
employees.stream().map(employee -> employee.getName().toLowerCase()).forEach(System.out::println);
}
提取对象的元素
/**
* map
* 提取元素
*/
@Test
public void test02(){
employees.stream().map(Employee::getName).distinct().forEach(System.out::println);
}
狸猫换太子
需要遍历map ,找到所有value(对象列表)添加新对象
// dailyMap,目前已有的map -- Map<String, List<CapitalDaily>>
Map<String, List<CapitalDaily>> map = dailyMap.entrySet().stream().map(entry -> {
String key = entry.getKey();
List<CapitalDaily> value = entry.getValue();
List<CapitalDaily> dailies = value.stream().filter(li -> null != li.getDate() && li.getDate().equals(date)).filter(i -> i.getBalance() != null).collect(Collectors.toList());
BigDecimal balance = dailies.get(dailies.size() - 1).getBalance();
BigDecimal creditAmount = value.stream().filter(li -> null != li.getDate() && li.getDate().equals(date)).filter(i -> i.getCreditAmount() != null).map(CapitalDaily::getCreditAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal debitAmount = value.stream().filter(li -> null != li.getDate() && li.getDate().equals(date)).filter(i -> i.getDebitAmount() != null).map(CapitalDaily::getDebitAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
CapitalDaily daily = new CapitalDaily();
// daily.setDate(date);
daily.setBalance(balance.equals(BigDecimal.valueOf(0)) ? null : balance);
daily.setCreditAmount(creditAmount.equals(BigDecimal.valueOf(0)) ? null : creditAmount);
daily.setDebitAmount(debitAmount.equals(BigDecimal.valueOf(0)) ? null : debitAmount);
ArrayList<CapitalDaily> newDaily = new ArrayList<>(value);
newDaily.add(daily);
return new AbstractMap.SimpleEntry<>(key, newDaily);
}).collect(Collectors.toMap(Map.Entry::<String, List<CapitalDaily>>getKey, Map.Entry::<String, List<CapitalDaily>>getValue));
return map;
2.5.2 flatMap映射
/**
* flatMap
* 将字符串拆分打印
*/
@Test
public void test03(){
Stream<Stream<Character>> streamStream = list.stream()
.map(Demo03_map::departString);
streamStream.forEach(s -> s.forEach(System.out::println));
System.out.println("=============== biu~~ =======================");
Stream<Character> characterStream = list.stream()
.flatMap(Demo03_map::departString);
characterStream.forEach(System.out::println);
}
/**
* 切割字符串的方法,返回一个流
* @Param str 要切割的字符串
* @Return characters.stream():Stream<Character>
*/
public static Stream<Character> departString(String str){
List<Character> characters = new ArrayList<>();
for (char c : str.toCharArray()) {
characters.add(c);
}
return characters.stream();
}
3. 终止操作
3.1 查找与匹配
3.1.1 allMatch 是否匹配所有元素
/**
* matchAll,是否匹配所有元素
*/
@Test
public void test01(){
boolean b = employees.stream()
.allMatch(employee -> employee.getAge() > 25);
System.out.println(b); // false
}
3.1.2 anyMatch 是否匹配至少一个元素
/**
* anyMatch 是否匹配到至少一个元素
*/
@Test
public void test02(){
boolean b = employees.stream()
.anyMatch(employee -> employee.getAge() > 25);
System.out.println(b); // true
}
3.1.3 noneMatch 是否没有匹配所有元素
/**
* noneMatch 是否没有匹配所有元素
*/
@Test
public void test03(){
boolean b = employees.stream()
.noneMatch(employee -> employee.getAge() > 25);
System.out.println(b); // false
}
@Test
public void test03_1(){
boolean b = employees.stream()
.noneMatch(employee -> employee.getAge() > 125);
System.out.println(b); // true
}
3.1.4 findFirst 返回第一元素
/**
* findFirst 返回第一元素
*/
@Test
public void test04(){
Optional<Employee> first = employees.stream()
.filter(employee -> employee.getAge() > 25)
.findFirst();
System.out.println(first); // false
}
3.1.5 findAny 返回任意元素
/**
* findAny 返回任意元素
*/
@Test
public void test05(){
Optional<Employee> first = employees.stream()
.filter(employee -> employee.getAge() < 25)
.findAny();
System.out.println(first);
}
3.1.6 count 返回流中元素总个数
/**
* count 返回流中元素总个数
*/
@Test
public void test06(){
long count = employees.stream().count();
System.out.println(count); // 7
}
3.1.7 max 返回流中最大值
/**
* max 返回流中最大值
*/
@Test
public void test07(){
Optional<Employee> max = employees.stream()
.max((e1, e2) -> e1.getAge() - e2.getAge());
System.out.println(max==null?"null":max.get().getAge()); // 93
}
3.1.8 min 返回流中最小值
/**
* min 返回流中最小值
*/
@Test
public void test08(){
Employee employee = employees.stream()
.min((e1, e2) -> e1.getAge() - e2.getAge()).orElse(null);
System.out.println(employee==null?"null":employee.getAge()); // 23
// Employee【id=2, name='SI LI', salary=4500.0, age=23】
System.out.println(employee);
}
3.2 归约和收集
3.2.1 reduce
reduce -- -- -- 将流中元素反复结合,得到一个新的值
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
/**
* 归约,求和
*/
@Test
public void test01(){
Integer sum = list.stream()
.reduce(1, (t, s) -> t + s);// 初始为1
System.out.println(sum); // 46
}
/**
* 获取员工工资总和
* map-reduce 模式
*/
@Test
public void test02(){
Double salarySum = employees.stream()
.map(Employee::getSalary)
// .reduce((double) 0, (s1, s2) -> s1 + s2);
.reduce(Double::sum).orElse(0.0D);
System.out.println(salarySum); // 44500.0
}
3.2.2 collectors
1 toList()/toSet()
/**
* 收集名字到list/set里
*/
@Test
public void test01() {
List<String> list = employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("===========================");
Set<String> set = employees.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
}
2 HashSet/LinkedHashSet
/**
* 收集名字到HashSet/LinkedHashSet里
*/
@Test
public void test02() {
Set<String> set = employees.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(LinkedHashSet::new));
set.forEach(System.out::println);
}
ArrayList<IdtCorpBasic> disList = list.stream().collect(
Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(IdtCorpBasic::getUscc))), ArrayList::new)
);
3 计数 count
/**
* 总数
*/
@Test
public void test03() {
Long count = employees.stream()
.map(Employee::getName)
.collect(Collectors.counting());
// [or] .count();
System.out.println(count); // 7
}
4 平均值 averagingDouble
/**
* 平均值
*/
@Test
public void test04() {
double count = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(count); // 6357.142857142857
System.out.println(String.format("0.00", count)); // 0.00
}
5 最大值 maxBy
/**
* 最大值
*/
@Test
public void test05() {
Employee employee = employees.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))).orElse(null);
System.out.println(employee.getSalary()); // 9000.0
6 最小值 minBy
/**
* 最小值
*/
@Test
public void test06() {
Double minSalary = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare)).orElse(null);
System.out.println(minSalary); // 3500.0
}
7 分组 groupingBy
/**
* 分组
*/
@Test
public void test07_1() {
Map<Integer, List<Employee>> byAge = employees.stream()
.collect(Collectors.groupingBy(Employee::getAge));
for (Map.Entry<Integer, List<Employee>> age : byAge.entrySet()) {
System.out.println(age.getKey() + ":" + age.getValue());
}
}
/**
* 多属性拼接为key
*/
@Test
public void test07_2() {
Map<String, List<Employee>> byIdAndName = employees.stream()
.collect(Collectors.groupingBy(e -> ((Employee) e).getId() + "--" + ((Employee) e).getName()));
for (Map.Entry<String, List<Employee>> map : byIdAndName.entrySet()) {
System.out.println(map.getKey() + ":" + map.getValue());
}
}
/**
* 多级分组
*/
@Test
public void test08() {
Map<String, Map<Object, List<Employee>>> levMap = employees.stream()
.collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy(e -> {
if (((Employee) e).getAge() <= 35) {
return "青年";
} else if (((Employee) e).getAge() <= 55) {
return "中年";
} else {
return "老年";
}
})));
System.out.println(levMap.getClass());// class java.util.HashMap
for (Map.Entry<String, Map<Object, List<Employee>>> lev : levMap.entrySet()) {
System.out.println(lev.getKey() + ":" + lev.getValue());
}
}
8 分区 partitioningBy
/**
* 分区
* 返回一个map,内部有key为true和false的两个对象列表
*/
@Test
public void test09(){
Map<Boolean, List<Employee>> depart = employees.stream()
.collect(Collectors.partitioningBy(e -> e.getAge() > 30)
);
for (Map.Entry<Boolean, List<Employee>> part : depart.entrySet()) {
System.out.println(part.getKey() + ":" + part.getValue());
}
/*
false:[Employee【id=2, name='SI LI', salary=4500.0, age=23】, Employee【id=4, name='SA ZHANG', salary=9000.0, age=25】, Employee【id=4, name='LIU ZHAO', salary=9000.0, age=25】]
true:[Employee【id=1, name='SA ZHANG', salary=3500.0, age=93】, Employee【id=1, name='SA ZHANG', salary=3500.0, age=93】, Employee【id=3, name='WU WANG', salary=6000.0, age=54】, Employee【id=5, name='QI A', salary=9000.0, age=35】]
*/
}
9 拼接 joining
/**
* 拼接
*/
@Test
public void test11(){
String names = employees.stream()
.map(Employee::getName)
// prefix和suffix是拼接之后,整体的前后缀
.collect(Collectors.joining(",", "=", "="));
System.out.println(names);
}