Bootstrap

Stream操作List

概述

Stream是Java8的一大亮点,是对容器对象功能的增强,它专注于对容器对象进行各种非常便利、高效的 聚合操作(aggregate operation)或者大批量数据操作。Stream API借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。
在这里插入图片描述

1. 流的常用创建方法:

//1.通过集合获取
Stream<User> stream = userList.stream();// 获取顺序流
Stream<User> userStream = userList.parallelStream();// 获取并行流(并发环境下会存在问题) -> userList = Collections.synchronizedList(new ArrayList<>());
//2.通过数组获取
int[] arr = new int[]{1,2,3,4,5,6};
Stream<Integer> intStream = (Stream<Integer>) Arrays.stream(arr);
//3.使用Stream中的静态方法:of()、iterate()、generate()
Stream<Integer> stream1 = Stream.of(1,2,3,4,5,6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);

2. 流的中间操作:

2.1 筛选与切片

  • filter:过滤流中的某些元素
  • limit(n):获取n个元素
  • skip(n):跳过n元素,配合limit(n)可实现分页
  • distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

2.2 映射

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

2.3 排序

  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):定制排序,自定义Comparator排序器

3. 流的终止操作

3.1 匹配、聚合操作

  • allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
  • noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
  • anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
  • findFirst:返回流中第一个元素,返回值Optional类型
  • findAny:返回流中的任意元素,返回值Optional类型
  • count:返回流中元素的总个数
  • max:返回流中元素最大值
  • min:返回流中元素最小值
//1.学生总数
Long count = list.stream().collect(Collectors.counting());
//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get(); 
//3.所有人的年龄
Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge));
//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge)); 
//5.总数+最大+最小+平均
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());

3.2 规约操作

  • Optional reduce(BinaryOperator accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素

3.3 收集操作

  • collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。
//装成list
List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList());
//转成set
Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet());
//转成map,注:key不能相同,否则报错
Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
//字符串分隔符连接
String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")"));
//分组
Map<Integer, List<User>> ageMap = list.stream().collect(Collectors.groupingBy(User::getAge));
List<User> users = ageMap.get(15);
//多重分组,先根据类型分再根据年龄分
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge)));

排序

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//@Data : 注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法
//@AllArgsConstructor : 注在类上,提供类的全参构造
//@NoArgsConstructor : 注在类上,提供类的无参构造
//@Setter : 注在属性上,提供 set 方法
//@Getter : 注在属性上,提供 get 方法
//@EqualsAndHashCode : 注在类上,提供对应的 equals 和 hashCode 方法

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private long id;
    private String name;
    private String username;
    private long pid;
    private int sort;
}

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class Test {
    public static void main(String[] args) {

        User u1 = new User(1L, "张三1", "张三111", 0L, 1);
        User u2 = new User(2L, "张三2", "张三222", 0L, 5);
        User u3 = new User(3L, "张三3", "张三333", 0L, 4);
        User u4 = new User(4L, "张三4", "张三444", 0L, 3);
        User u5 = new User(5L, "张三5", "张三555", 0L, 2);
        // 集合就是存储
        List<User> userList = Arrays.asList(u1, u2, u3, u4, u5);

        // 排序(升序)
        List<User> collect = userList.stream().sorted(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getSort() - o2.getSort();
            }
        }).collect(Collectors.toList());

        collect.forEach(System.out::println);
    }
}
//返回 对象集合以类属性一升序排序
list.stream().sorted(Comparator.comparing(::属性一));

//返回 对象集合以类属性一降序排序 注意两种写法
list.stream().sorted(Comparator.comparing(::属性一).reversed());//先以属性一升序,结果进行属性一降序
list.stream().sorted(Comparator.comparing(::属性一,Comparator.reverseOrder()));//以属性一降序

//返回 对象集合以类属性一升序 属性二升序
list.stream().sorted(Comparator.comparing(::属性一).thenComparing(::属性二));

//返回 对象集合以类属性一降序 属性二升序 注意两种写法
list.stream().sorted(Comparator.comparing(::属性一).reversed().thenComparing(::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序
list.stream().sorted(Comparator.comparing(::属性一,Comparator.reverseOrder()).thenComparing(::属性二));//先以属性一降序,再进行属性二升序
 

过滤

// 过滤符合条件的元素返回集合
List<User> collect = userList.stream().filter(user -> user.getId().equals(2L)).collect(Collectors.toList());

map: 改变集合中每个元素的信息

//添加__ssss的后缀
List<String> userList1 = userList.stream().map(user -> user.getName().concat("__ssss")).collect(Collectors.toList());

reduce:计算

// map + reduce 求和集合中sort这一列的总和
Integer reduce = userList.stream().map(User::getSort).reduce(0, (o1, o2) -> o1 + o2);
// 购物车: 单价*数量
Long reduce1 = userList.stream().map(user -> {
    return user.getSort()*user.getId();
}).reduce(0L, (o1, o2) -> o1 + o2);
//添加__ssss的后缀
List<User> userList1 = userList.stream().map(user -> {
    user.setName(user.getName().concat("_ssss"));
    return user;
}).collect(Collectors.toList());

forEach循环遍历

//forEach
userList.stream().forEach(user -> {
    System.out.println(user);
});
// 简写
userList.forEach(System.out::println);

普通去重

protected final <T> List<T> removeDuplicates(List<T> list) {
    return new ArrayList(new LinkedHashSet(list));
}

distinct去重

distinct可以去重,如果集合是对象,就需要对象返回相同的hashcode并且equals方法的返回值是true。
在这里插入图片描述

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private long id;
    private String name;
    private String username;
    private long pid;
    private int sort;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id &&
                pid == user.pid &&
                sort == user.sort &&
                Objects.equals(name, user.name) &&
                Objects.equals(username, user.username);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, username, pid, sort);
    }
}

//distinct
List<User> userList= userList.stream().distinct().collect(Collectors.toList());
// 集合根据实体类中的单个字段姓名name去重复
userList = userList.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))),
ArrayList::new)
);
// 根据实体类中多个属性去重复
 seqAloneFieldList = markDefEntity.getMarkFieldEntityList().stream().filter(item -> item.isSeqAlone() == true && item.getMarkFieldType().equals(MarkFieldTypeEnum.SERIAL_NUMBER) == false)
                    .sorted(Comparator.comparing(MarkFieldEntity::getMarkFieldType).thenComparing(MarkFieldEntity::getRelateObjValue))
                    .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getMarkFieldType() + ";" + o.getRelateObjValue()))), ArrayList::new));

max & min

// max & min
Optional<User> maxUserOptional = userList.stream().max(new Comparator<User>() {
    @Override
    public int compare(User o1, User o2) {
        return o1.getSort() - o2.getSort();
    }
});
if(maxUserOptional.isPresent()){
    System.out.println(maxUserOptional.get());
}

findFirst: 返回集合中的第一个元素

// findFirst
Optional<User> firstUserOptional = userList.stream().findFirst();
if(firstUserOptional.isPresent()){
    System.out.println(firstUserOptional.get());
}

groupingBy分组器

  • 对List作分组操做
// 根据某一个属性分组
Map<String, List<Employee>> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity));
  • 统计每一个分组的count
Map<String, Long> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.counting()));
    System.out.println(employeesByCity);
    assertEquals(employeesByCity.get("London").longValue(), 2L);
  • groupingBy操做计算List集合计算分组平均值
Map<String, Double> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.averagingInt(Employee::getSales)));
    System.out.println(employeesByCity);
    assertEquals(employeesByCity.get("London").intValue(), 175);
  • groupingBy操做计算总和sum
Map<String, Long> employeesByCity =
            employees.stream().collect(Collectors.groupingBy(Employee::getCity, Collectors.summingLong(Employee::getSales)));

分页

List<T> pageList = list.stream().sorted()
			.skip((pageNum - 1) * pageSize) // 跳过前面多少条
            .limit(pageSize)// 从(pageNum - 1) * pageSize之后取出pageSize条
            .collect(Collectors.toList());
// 处理最终返回前5条,后面6条
if(preCodeList.size() > 11 ) {
    List<String> preFiveList = preCodeList.stream().limit(5).collect(Collectors.toList());
    List<String> lastSixList = preCodeList.stream().skip(preCodeList.size()-6).collect(Collectors.toList());
    preCodeList.clear();
    preCodeList.addAll(preFiveList);
    preCodeList.addAll(lastSixList);
}

removeIf

userList.removeIf(user -> user.getId() == 0);
;