【196期】夯实基础,Java8新特性Stream详细教程
阅读本文大概需要 16 分钟。
来自:blog.csdn.net/mu_wind/article/details/91464351
1 基本特性
1.1 流的特性
stream
不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果;stream
不会改变数据源,通常情况下会产生一个新的集合;stream
具有延迟执行特性,只有调用终端操作时,中间操作才会执行。对
stream
操作分为终端操作和中间操作,那么这两者分别代表什么呢?
终端操作:会消费流,这种操作会产生一个结果的,如果一个流被消费过了,那它就不能被重用的。
中间操作:中间操作会产生另一个流。因此中间操作可以用来创建执行一系列动作的管道。一个特别需要注意的点是:中间操作不是立即发生的。相反,当在中间操作创建的新流上执行完终端操作后,中间操作指定的操作才会发生。所以中间操作是延迟发生的,中间操作的延迟行为主要是让流API能够更加高效地执行。stream
不可复用,对一个已经进行过终端操作的流再次调用,会抛出异常。
1.2 创建Stream
public static void main(String[] args) {
//1.通过Arrays.stream
//1.1基本类型
int[] arr = new int[]{1,2,34,5};
IntStream intStream = Arrays.stream(arr);
//1.2引用类型
Student[] studentArr = new Student[]{new Student("s1",29),new Student("s2",27)};
Stream<Student> studentStream = Arrays.stream(studentArr);
//2.通过Stream.of
Stream<Integer> stream1 = Stream.of(1,2,34,5,65);
//注意生成的是int[]的流
Stream<int[]> stream2 = Stream.of(arr,arr);
stream2.forEach(System.out::println);
}
public static void main(String[] args) {
List<String> strs = Arrays.asList("11212","dfd","2323","dfhgf");
//创建普通流
Stream<String> stream = strs.stream();
//创建并行流
Stream<String> stream1 = strs.parallelStream();
}
2 流API详述
2.1 BaseStream详述
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
Iterator<T> iterator();
Spliterator<T> spliterator();
boolean isParallel();
S sequential();
S parallel();
S unordered();
S onClose(Runnable closeHandler);
@Override
void close();
}
2.2 Stream详述
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
<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<T> distinct();
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> peek(Consumer<? super T> action);
Stream<T> limit(long maxSize);
Stream<T> skip(long n);
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
Optional<T> findFirst();
Optional<T> findAny();
// Static factories
public static<T> Builder<T> builder() {
return new Streams.StreamBuilderImpl<>();
}
public static<T> Stream<T> empty() {
return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
}
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
public static<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
}
3 常用方法
3.1 演示所用数据
public class Person {
private String name;
private int salary;
// 构造方法
public Person(String name, int salary) {
this.name = name;
this.salary = salary;
}
// 省略get与set方法
}
public class MyTest {
public static void main(String[] args) {
List<Person> personList= new ArrayList<Person>();
persons.add(new Person("Tom", 8900));
persons.add(new Person("Jack", 7000));
persons.add(new Person("Lily", 9000));
}
}
3.2 筛选和匹配
普通类型筛选
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
List<Integer> collect = intList.stream().filter(x -> x > 7).collect(Collectors.toList());
System.out.println(collect);
}
// 预期结果:
[8,9]
public static void main(String[] args) {
List<Person> collect = personList.stream().filter(x -> x.getSalary() > 8000).collect(Collectors.toList());
// 预期结果:符合条件的实体类的集合
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(7,6,9,3,8,2,1);
// 匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
// 匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
// 是否包含
boolean anyMatch = list.stream().anyMatch(x -> x < 6);
System.out.println(findFirst);
System.out.println(findAny);
System.out.println(anyMatch);
}
}
// 预期结果:
// 1、Optional[7]
// 2、并行流处理,结果不确定
// 3、true
3.3 聚合
3.3.1 max、min和count
public static void main(String[] args) {
List<String> list = Arrays.asList("adnm","admmt","pot");
Optional<String> max = list.stream().max(Comparator.comparing(String::length));
System.out.println(max);
}
// 预期结果:
Optional[admmt]
public static void main(String[] args) {
List<Integer> list = Arrays.asList(7,6,9);
Optional<Integer> reduce = list.stream().max(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
System.out.println(reduce);
}
//输出结果:
Optional[9]
public static void main(String[] args) {
list.add(new Person("a", 4));
list.add(new Person("b", 4));
list.add(new Person("c", 6));
Optional<Person> max = list.stream().max(Comparator.comparingInt(Person::getSalary));
System.out.println(max.get().getSalary());
}
// 输出结果:6,最小值将max改为min即可
public static void main(String[] args) {
List<Integer> list = Arrays.asList(7,6,9);
long count = list.stream().filter(x -> x > 6).count();
System.out.println(count);
}
// 预期结果:2
3.3.2 缩减(reduce)
public interface Stream<T> extends BaseStream<T, Stream<T>> {
// 方法1
T reduce(T identity, BinaryOperator<T> accumulator);
// 方法2
Optional<T> reduce(BinaryOperator<T> accumulator);
// 方法3
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
}
下面用一些示例来演示前两种reduce的用法:
public static void main(String[] args) throws Exception {
List<Integer> list = Arrays.asList(1, 3, 2);
// 求和
Integer sum = list.stream().reduce(1, (x, y) -> x + y);
// 结果是7,也就是list元素求和再加上1
System.out.println(sum);
// 写法2
Integer sum2 = list.stream().reduce(1, Integer::sum);
System.out.println(sum2); // 结果:7
// 求最值
Integer max = list.stream().reduce(6, (x, y) -> x > y ? x : y);
System.out.println(max); // 结果:6
// 写法2
Integer max2 = list.stream().reduce(1, Integer::max);
System.out.println(max2); // 结果:3
}
public class MyTest {
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
// 求和
// 预期结果:Optional[24900]
System.out.println(personList.stream().map(Person::getSalary).reduce(Integer::sum));
// 求最值-方式1
Person person = personList.stream().reduce((p1, p2) -> p1.getSalary() > p2.getSalary() ? p1 : p2).get();
// 预期结果:Lily:9000
System.out.println(person.getName() + ":" + person.getSalary());
// 求最值-方式2
// 预期结果:Optional[9000]
System.out.println(personList.stream().map(Person::getSalary).reduce(Integer::max));
// 求最值-方式3:
System.out.println(personList.stream().max(Comparator.comparingInt(Person::getSalary)).get().getSalary());
}
}
下面用对象集合求和和求最大值的实例来演示第三种缩减操作的用法:
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
// 求和-方式1
Integer sumSalary = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),
(sum1, sum2) -> sum1 + sum2);
System.out.println(sumSalary); // 24900
// 求和-方式2
Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);
System.out.println(sumSalary2); // 24900
// 求最大值-方式1
Integer maxSalary = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(),Integer::max);
System.out.println(maxSalary); // 9000
// 求最大值-方式2
Integer maxSalary2 = personList.stream().reduce((max, p) -> max > p.getSalary() ? max : p.getSalary(),(max1, max2) -> max1 > max2 ? max1 : max2);
System.out.println(maxSalary2); // 9000
}
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
// 验证combiner-串行流
Integer sumSalary = personList.stream().reduce(0, (sum, p) -> {
System.out.format("accumulator: sum=%s; person=%s\n", sum, p.getName());
return sum += p.getSalary();
} , (sum1, sum2) -> {
System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2);
return sum1 + sum2;
});
System.out.println("总和:" + sumSalary);
// 输出结果:
// accumulator: sum=0; person=Tom
// accumulator: sum=8900; person=Jack
// accumulator: sum=15900; person=Lily
// 总和:24900
// 验证combiner-并行流
Integer sumSalary2 = personList.parallelStream().reduce(0, (sum, p) -> {
System.out.format("accumulator: sum=%s; person=%s\n", sum, p.getName());
return sum += p.getSalary();
} , (sum1, sum2) -> {
System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2);
return sum1 + sum2;
});
System.out.println("总和:" + sumSalary2);
// 输出结果:
// accumulator: sum=0; person=Jack
// accumulator: sum=0; person=Tom
// accumulator: sum=0; person=Lily
// combiner: sum1=7000; sum2=9000
// combiner: sum1=8900; sum2=16000
// 总和:24900
}
3.3.3 收集(collect)
public interface Stream<T> extends BaseStream<T, Stream<T>> {
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
}
1、averaging系列
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
Double averageSalary = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
System.out.println(averageSalary); // 结果:8300
}
2、summarizing系列
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
System.out.println(collect);
// 输出结果:
// DoubleSummaryStatistics{count=3, sum=24900.000000, min=7000.000000, average=8300.000000, max=9000.000000}
}
3、joining
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
String names = personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
System.out.println(names);
}
4、reduce
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
Integer sumSalary = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -> i + j));
System.out.println(sumSalary); // 结果:24900
Optional<Integer> sumSalary2 = list.stream().map(Person::getSalary).reduce(Integer::sum);
System.out.println(sumSalary2); // Optional[24900]
}
5、groupingBy
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
// 单级分组
Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getName));
System.out.println(group);
// 输出结果:{Tom=[mutest.Person@7cca494b],
// Jack=[mutest.Person@7ba4f24f],Lily=[mutest.Person@3b9a45b3]}
// 多级分组:先以name分组,再以salary分组:
Map<String, Map<Integer, List<Person>>> group2 = personList.stream()
.collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getSalary)));
System.out.println(group2);
// 输出结果:{Tom={8900=[mutest.Person@7cca494b]},Jack={7000=[mutest.Person@7ba4f24f]},Lily={9000=[mutest.Person@3b9a45b3]}}
}
6、toList、toSet、toMap
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900));
personList.add(new Person("Jack", 7000));
personList.add(new Person("Lily", 9000));
personList.add(new Person("Lily", 5000));
// toList
List<String> names = personList.stream().map(Person::getName).collect(Collectors.toList());
System.out.println(names);
// toSet
Set<String> names2 = personList.stream().map(Person::getName).collect(Collectors.toSet());
System.out.println(names2);
// toMap
Map<String, Person> personMap = personList.stream().collect(Collectors.toMap(Person::getName, p -> p));
System.out.println(personMap);
}
3.4 映射(map)
1、数据>>数据
public static void main(String[] args) {
String[] strArr = { "abcd", "bcdd", "defde", "ftr" };
Arrays.stream(strArr).map(x -> x.toUpperCase()).forEach(System.out::println);
}
// 预期结果:
ABCD BCDD DEFDE FTR
2、对象集合>>数据
public static void main(String[] args) {
// 为节省篇幅,personList复用了演示数据中的personList
personList.stream().map(person -> person.getSalary()).forEach(System.out::println);
}
// 预期结果:
ABCD BCDD DEFDE FTR
3、对象集合>>对象集合
public static void main(String[] args) {
// 为节省篇幅,personList复用了演示数据中的personList
List<Person> collect = personList.stream().map(person -> {
person.setName(person.getName());
person.setSalary(person.getSalary() + 10000);
return person;
}).collect(Collectors.toList());
System.out.println(collect.get(0).getSalary());
System.out.println(personList.get(0).getSalary());
List<Person> collect2 = personList.stream().map(person -> {
Person personNew = new Person(null, 0);
personNew.setName(person.getName());
personNew.setSalary(person.getSalary() + 10000);
return personNew;
}).collect(Collectors.toList());
System.out.println(collect2.get(0).getSalary());
System.out.println(personList.get(0).getSalary());
}
// 预期结果:
// 1、18900 18900,说明这种写法改变了原有的personList。
// 2、18900 8900,说明这种写法并未改变原有personList。
3.5 排序(sorted)
public static void main(String[] args) {
String[] strArr = { "abc", "m", "M", "bcd" };
System.out.println(Arrays.stream(strArr).sorted().collect(Collectors.toList()));
}
// 预期结果:
[M, abc, bcd, m]
public static void main(String[] args) {
String[] strArr = { "ab", "bcdd", "defde", "ftr" };
// 1、按长度自然排序,即长度从小到大
Arrays.stream(strArr).sorted(Comparator.comparing(String::length)).forEach(System.out::println);
// 2、按长度倒序,即长度从大到小
Arrays.stream(strArr).sorted(Comparator.comparing(String::length).reversed()).forEach(System.out::println);
// 3、首字母倒序
Arrays.stream(strArr).sorted(Comparator.reverseOrder()).forEach(System.out::println);
// 4、首字母自然排序
Arrays.stream(strArr).sorted(Comparator.naturalOrder()).forEach(System.out::println);
}
/**
* thenComparing
* 先按照首字母排序
* 之后按照String的长度排序
*/
@Test
public void testSorted3_(){
Arrays.stream(arr1).sorted(Comparator.comparing(this::com1).thenComparing(String::length)).forEa
ch(System.out::println);
}
public char com1(String x){
return x.charAt(0);
}
// 输出结果:
// 1、ftr bcdd defde
// 2、defde bcdd ftr ab
// 3、ftr defde bcdd ab
// 4、ab bcdd defde ftr
3.6 提取流和组合流
public static void main(String[] args) {
String[] arr1 = {"a","b","c","d"};
String[] arr2 = {"d","e","f","g"};
String[] arr3 = {"i","j","k","l"};
/**
* 可以把两个stream合并成一个stream(合并的stream类型必须相同),只能两两合并
* 预期结果:a b c d e(为节省篇幅,空格代替换行)
*/
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
Stream.concat(stream1,stream2).distinct().forEach(System.out::println);
/**
* limit,限制从流中获得前n个数据
* 预期结果:1 3 5 7 9 11 13 15 17 19
*/
Stream.iterate(1,x->x+2).limit(10).forEach(System.out::println);
/**
* skip,跳过前n个数据
* 预期结果:3 5 7 9 11
*/
Stream.iterate(1,x->x+2).skip(1).limit(5).forEach(System.out::println);
}
推荐阅读:
【195期】MySQL中的条件判断函数 CASE WHEN、IF、IFNULL你会用吗?
【194期】Redis——第三方jar没有封装的命令我们该怎么执行?
【193期】如何利用装饰者模式在不改变原有对象的基础上扩展功能
微信扫描二维码,关注我的公众号
朕已阅