一、流操作
首先,定义一个基础的对象,用于测试:
@Data
@AllArgsConstructor
static class Student {
private String name;
private Integer age;
private String address;
private Integer rank;
private String sex;
private Double money;
}
Student a = new Student("A", 9, "1qaz", 3, "m", 15.3);
Student b = new Student("B", 15, "1qaz", 2, "m", 23.7);
Student c = new Student("C", 12, "1qaz", 1, "w", 6.4);
List<Student> list = new ArrayList<>();
list.add(a);
list.add(b);
list.add(c);
1.1、map映射
// 按照姓名进行映射 map,key是学生姓名,value是学生对象
Map<String, Student> map1 = list.stream().collect(
Collectors.toMap(
Student::getName,
Function.identity(),
(k1, k2) -> k1
)
);
// 按照姓名进行映射 map,map的 key是学生姓名, value是对象的 sex属性
Map<String, String> map2 = list.stream().collect(
Collectors.toMap(
Student::getName,
Student::getSex,
(k1, k2) -> k1
)
);
// 指定收集的 map class类型
map2 = list.stream().collect(
Collectors.toMap(
Student::getName,
Student::getSex,
(k1, k2) -> k1,
LinkedHashMap::new
)
);
// 按照性别进行分组,分组的 key是学生性别,value是对应性别的学生对象集合
Map<String, List<Student>> map3 = list.stream().collect(
Collectors.groupingBy(Student::getSex)
);
// 按照性别进行分组,分组的 key是学生性别,value是对应性别的学生姓名集合
Map<String, List<String>> map4 = list.stream().collect(
Collectors.groupingBy(
Student::getSex,
Collectors.mapping(Student::getName, Collectors.toList())
)
);
// 计算不同性别的学生,拥有的金额总数
Map<String, Double> map5 = list.stream().collect(
Collectors.groupingBy(
Student::getSex,
Collectors.summingDouble(Student::getMoney)
)
);
// 按照学生年龄在 10岁以下,10岁以上进行分组
Map<Boolean, List<Student>> map6 = list.stream().collect(
Collectors.partitioningBy(s -> s.getAge() >= 10)
);
// 多重分组:先按照学生的性别进行分组,再按照姓名进行分组
Map<String, Map<String, List<Student>>> map7
= list.stream().collect(
Collectors.groupingBy(
Student::getSex,
Collectors.groupingBy(Student::getName)
)
);
1.2、合并计算
// 计算所有学生的金额总数
// list.stream().collect(Collectors.summingDouble(Student::getMoney))
double total = list.stream().mapToDouble(Student::getMoney).sum();
// 将所有学生的姓名用 , 连接起来
String names = list.stream().map(Student::getName).collect(Collectors.joining(", "));
1.3、list属性转换
// 获取所有学生的地址信息
List<String> addressList = list.stream().map(Student::getAddress)
.distinct()
.collect(Collectors.toList());
1.4、排序
// 按照学生的年龄进行升序排序
List<Student> list1 = list.stream()
.sorted(Comparator.comparingInt(Student::getAge))
.collect(Collectors.toList());
// 按照学生的金钱进行降序排序
List<Student> list2 = list.stream()
.sorted(Comparator.comparingDouble(Student::getMoney)
.reversed())
.collect(Collectors.toList());
// 按照学生的年龄进行升序排序,再按照学生的金钱进行降序排序
List<Student> list3 = list.stream().sorted(
Comparator.comparingInt(Student::getAge)
.thenComparing(Student::getMoney, Comparator.reverseOrder()))
.collect(Collectors.toList());
1.5、Stream 流操作类型
流的操作类型分为两种:
- Intermediate:一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
- Terminal:一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以,这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果。
List<String> list4 = list.stream()
.filter(stu -> {
System.out.println("filter "+ stu.getName());
return stu.age > 10;
})
.map(apple -> {
System.out.println("mapping " + apple.getName());
return apple.name;
})
.limit(2)
.collect(Collectors.toList());
输出的结果:
filter A
filter B
mapping B
filter C
mapping C
从上面的结果可以看出,Intermediate流操作类型也是分为 2种类型的。
- Intermediate A流操作下紧接是 Intermediate B流操作,B的计算不依赖于 A的数据转换结果,可以将 A B一起执行
- Intermediate A流操作下紧接是 Intermediate B流操作,B的计算依赖于 A的数据转换结果,B的执行需要等待 A执行完毕。
1.5.1、流的构造与转换:
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
1.5.2、常规的数值型聚合运算
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);
1.5.3、流也可以转换为其它数据结构
// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();
1.6、Stream类的 API
public interface Stream<T> extends BaseStream<T, Stream<T>> {
/**
* 过滤并返回 predicate为 true的 Stream里的元素;
* eg:
* List<Apple> appleList =
* list.stream().filter(apple -> apple.num > 10).collect(Collectors.toList());
* @return the new stream
*/
Stream<T> filter(Predicate<? super T> predicate);
/**
* 根据 mapper处理 Stream流数据,map生成的是个 1:1映射,并将其返回值作为新的 Stream流对象
* eg:
* List<String> appleNameList = list.stream().map(Apple::getName).collect(Collectors.toList());
* @return the new stream
*/
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
/**
* 返回 IntStream包装流,可以进行一些快捷的数值操作
* eg:
* int sum = appleList.stream().mapToInt(Apple::getNum).sum();
* OptionalInt optionalInt = appleList.stream().mapToInt(Apple::getNum).max();
* // 获取最大值前,需要去校验下 OptionalInt的真值
* int max = optionalInt.isPresent() ? optionalInt.getAsInt() : -1;
* @return the new stream
*/
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
/**
* flatMap把 Stream中的层级结构 扁平化,就是将最底层元素抽出来放到一起
* eg:
* Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3),
* Arrays.asList(4, 5, 6));
* Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());
* @return the new stream
*/
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
/**
* 过滤 Stream里面重复的元素,并返回去重结果的 Stream流
* eg:
* List<Integer> idList = appleList.stream().map(Apple::getId).distinct().collect(Collectors.toList());
*
* @return the new stream
*/
Stream<T> distinct();
/**
* 返回流对象里面元素排序后的新的流对象, 按照流里面元素的默认排序规则
* 若元素不支持排序,则会抛出 java.lang.ClassCastException异常
* eg:
* appleList.stream().sorted()
* @return the new stream
*/
Stream<T> sorted();
/**
* 按照自定义的排序规则进行排序
* eg: (Comparator工具类或者自定义 Comparator对象)
* appleList.stream().sorted(Comparator.comparing(Apple::hashCode));
* appleList.stream().sorted((a, b) -> a.num - b.num);
* @return the new stream
*/
Stream<T> sorted(Comparator<? super T> comparator);
/**
* 属于 intermediate操作,返回新的流。类似于 foreach操作,但是 foreach操作是 termediate操作
* eg:
* appleList.stream().peek(System.out::println).map(Apple::getName)
* @return the new stream
*/
Stream<T> peek(Consumer<? super T> action);
/**
* 从流中返回前 maxSize的元素组成的新的流,如果 maxSize超过原流的元素长度,则抛出异常
* eg:
* appleList.stream().limit(2).map(Apple::getName)
* @return the new stream
*/
Stream<T> limit(long maxSize);
/**
* 从流中去除前 n个元素,剩下的元素组成的新的流,如果 n超过原流的元素长度,则返回空的流对象
* eg:
* // 取第 3-5个流元素
* appleList.stream().limit(5).skip(2).map(Apple::getName)
* @return the new stream
*/
Stream<T> skip(long n);
void forEach(Consumer<? super T> action);
/**
* 与 foreach的主要区别在于并行流的处理上,forEachOrdered遍历流元素顺序与流元素的顺序严格一致
* eg:
* appleList.stream().parallel().forEachOrdered(Apple::getName);
*/
void forEachOrdered(Consumer<? super T> action);
/**
* 返回一个由流元素组成的 Object数组
*
* eg:
* Object[] arr = appleList.stream.map(Apple::getName).toArray();
* @return the object array
*/
Object[] toArray();
/**
* 返回一个由流元素组成的 指定类型数组
*
* eg:
* String[] arr = appleList.stream.map(Apple::getName).toArray(String[]::new);
* @return A array
*/
<A> A[] toArray(IntFunction<A[]> generator);
/**
* 提供一个起始值,按照运算规则 BinaryOperator和 Stream的每个元素依次进行组合,返回最终结果
* 从这个意义上说,字符串拼接、数值的 sum、min、max都是特殊的reduce。
* eg:
* // 求和
* appleList.stream().map(Apple::getNum).reduce(0, (sum, curr) -> sum + curr);
* appleList.stream().map(Apple::getNum).reduce(0, Integer::sum);
* appleList.stream().mapToInt(Apple::getNum).sum();
* // 字符串连接
* appleList.stream().map(Apple::getName).reduce("", (a, b) -> a + b);
* appleList.stream().map(Apple::getName).reduce("", String::concat);
* // 求最小值
* appleList.stream().map(Apple::getNum).reduce(Integer.MAX_VALUE, Integer::min);
* // 求最大值
* appleList.stream().map(Apple::getNum).reduce(Integer.MIN_VALUE, Integer::max);
*
* @return 组合结果
*/
T reduce(T identity, BinaryOperator<T> accumulator);
/**
* 不提供起始值,按照运算规则 BinaryOperator和 Stream的每个元素依次进行组合,返回最终结果
* eg:
* // 求和
* Optional<Integer> optional = appleList.stream().map(Apple::getNum).reduce(0, Integer::sum);
* int sumNum = optional.isPresent() ? optional.get() : 0;
*
* @return 组合结果
*/
Optional<T> reduce(BinaryOperator<T> accumulator);
<R, A> R collect(Collector<? super T, A, R> collector);
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
/**
* 返回流里面的元素个数
* appleList.stream().count() 就等于下面的语句
* appleList.stream().mapToLong(apple -> 1L).sum();
* @return 元素个数
*/
long count();
/**
* Stream 中全部元素符合传入的 predicate,返回 true
* eg:
* appleList.stream().map(Apple::getNum).anyMatch(num -> num > 15);
*
* @return boolean
*/
boolean anyMatch(Predicate<? super T> predicate);
/**
* Stream 中只要有一个元素符合传入的 predicate,返回 true
* eg:
* appleList.stream().map(Apple::getNum).anyMatch(num -> num > 15);
*
* @return boolean
*/
boolean allMatch(Predicate<? super T> predicate);
/**
* Stream 中没有一个元素符合传入的 predicate,返回 true
* eg:
* appleList.stream().map(Apple::getNum).noneMatch(num -> num > 15);
*
* @return boolean
*/
boolean noneMatch(Predicate<? super T> predicate);
/**
* 返回Stream的第一个元素或者空
* eg:
* appleList.stream().findFirst().orElse(null);
*
* @return 第一个元素
*/
Optional<T> findFirst();
/**
* 返回Stream的某已个元素,若流为空则返回空
* eg:
* appleList.stream().findAny().orElse(null);
*
* @return 第一个元素
*/
Optional<T> findAny();
}