Stream让开发者能够以一种声明的方式处理数据源(集合、数组等)
Stream专注于对数据源进行各种高效的聚合操作和大批量数据操作
Stream API 处理的数据源看着一种Stream(流),Stream在Pipeline(管道)中传输和运算(筛选、排序、聚合)后得到最终的处理结果
Stream API的使用离不开lambda表达式,可以提高编码效率和代码可读性
Stream API提供串行和并行两种操作,并行操作能发挥多核处理优势,使用fork/join的方式进行并行操作以提高运行速度。并行操作无需编写多线程代码即可写出高效的并发程序,且可避免多线程代码出错的问题
在新版的JPA中,也已经加入了Stram
@Query("select u from User u") Stream<User> findAllByCustomQueryAndStream();
元素 Stream是一个来自数据源的元素队列,Stream本身并不存储元素。
数据源(即Stream的来源)包含集合、数组、I/O channel、generator(发生器)等。
聚合操作 类似SQL中的filter、map、find、match、sorted等操作
管道运算 Stream在Pipeline中运算后返回Stream对象本身,这样多个操作串联成一个Pipeline,并形成fluent风格的代码。这种方式可以优化操作,如延迟执行(laziness)和短路( short-circuiting)。
内部迭代 不同于java8以前对集合的遍历方式(外部迭代),Stream API采用访问者模式(Visitor)实现了内部迭代。
并行运算 Stream API支持串行(stream() )或并行(parallelStream() )的两种操作方式。
- 创建Stream:从一个数据源(集合、数组)中获取流
- 中间操作:一个操作的中间链,对数据源的数据进行操作
- 终止操作:一个终止操作,执行中间操作链,并产生结果(操作完需要关闭)
创建Stream
#集合创建流
List list = new arraryList();
list.add(a);
list.add(b);
list.add(c);
Stream<list> stream = list.stream();
#数组创建流
String[] arrary = new String(){a,b,c}
Stram<String> stream = Stream.of(arrary);
中间操作
终止操作–查找与匹配
stream()——串行流
parallelStream()——并行流,使用parallelStream()生成并行流后,对集合元素的遍历是无序的
看源码
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
看列子
public static void main(String[] args)
{
List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
long count = numbers.parallelStream().filter(i -> i>0).count();
System.out.println("Positive count: " + count);
}
package java.util.stream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
public interface Stream<T> extends BaseStream<T, Stream<T>> {
//filter()方法的参数为Predicate(函数式接口)对象,用它进行过滤
Stream<T> filter(Predicate<? super T> predicate);
//map()方法的参数为Function(函数式接口)对象,map()方法将流中的所有元素用Function对象进行运算,生成新的流对象(流的元素类型可能改变)
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//summaryStatistics()方法进行数值统计。其实summaryStatistics()方法并不是Stream接口的方法,而是Stream API采用mapToInt()、mapToLong()、mapToDouble()三个方法分别生成IntStream 、LongStream 、DoubleStream 三个接口类型的对象,这个方法的参数分别为3个函数式接口ToIntFunction、ToLongFunction、ToDoubleFunction,使用时可以用lambda表达式计算返回对应的int、long、double类型即可。IntStream 、LongStream 、DoubleStream 三个接口类型都有一个summaryStatistics()方法,方法对应的类型分别是:IntSummaryStatistics、LongSummaryStatistics 、DoubleSummaryStatistics ,都有诸如统计数量、最大值、最小值、求和、平均值等方法(方法名和返回类型可能不同),利用这些方法我们可以方便的进行数值统计,列如:
// public static void main(String[] args)
// List numbers = Arrays.asList(-1, -2, 0, 4, 5);
// IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
// System.out.println("Max : " + stats.getMax());
// System.out.println("Min : " + stats.getMin());
// System.out.println("Sum : " + stats.getSum());
// System.out.println("Average : " + stats.getAverage());
// System.out.println("Count : " + stats.getCount());}
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
//跟map()方法不同的是,Function函数的返回值类型是Stream extends R>类型,而不是R类型,即Function函数返回一个Stream流,这样flatMap()能够将一个二维的集合映射成一个一维的集合,比map()方法拥有更高的映射深度(此处可能有一点绕,可结合例子理解)
//有一个字符串数组:List list = Arrays.asList("1 2", "3 4", "5 6");
//其有三个元素,每个元素有两个数组并用空格隔开,如果每个元素以空格分割成2个元素,并遍历打印这6个元素,
//用flatMap()方法如下:
//list.stream().flatMap(item -> Arrays.stream(item.split(" "))).forEach(System.out::println);
//而用map()方法:
//list.stream().map(item -> Arrays.stream(item.split(" "))).forEach(n ->n.forEach(System.out::println));
//可见,用map()方法,返回了一个“流中流”,需要在每个Stream元素遍历时,再加一层forEach进行遍历。
<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);
//去重,筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
Stream<T> distinct();
//sorted()--自然排序(Comparable)
Stream<T> sorted();
//sorted(Comparator com)--定制排序(Comparator),匿名类部类,列如:
//final Stream sorted = personList.stream().sorted((p1, p2) -> {
// if (p1.getAge().equals(p2.getAge())) {
// return p1.getName().compareTo(p2.getName());
// } else {
// return p1.getAge().compareTo(p2.getAge());
// }
//});
//sorted.forEach(System.out::println);
Stream<T> sorted(Comparator<? super T> comparator);
//生成一个包含原Stream的所有元素的新Stream,并指定消费函数。peek()方法提供Consumer(消费)函数,但执行peek()方法时不会执行Consumer函数,而是等到流真正被消费时(终端操作时才进行消费)才会执行,这种操作为中间操作
Stream<T> peek(Consumer<? super T> action);
//截断流,使其元素不超过给定对象
Stream<T> limit(long maxSize);
//跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
Stream<T> skip(long n);
//forEach()方法的参数为一个Consumer(消费函数,一个函数式接口)对象,forEach()方法用来迭代流中的每一个数据
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
// reduce操作又称为折叠操作,用于将流中的所有值合成一个。。reduce()方法参数为BinaryOperator类型的累加器(它接受两个类型相同的参数,返回值类型跟参数类型相同),返回一个Optional对象。实际上,Stream API中的mapToInt()方法返回的IntStream接口有类似的 average()、count()、sum()等方法就是做reduce操作,类似的还有mapToLong()、mapToDouble() 方法。当然,我们也可以用reduce()方法来自定义reduce操作。例如我们用reduce()方法来进行整数数组求和操作:
// public static void main(String[] args) {
// List numbers = Arrays.asList(-1, -2, 0, -1, 4, 5, 1);
// Integer total = numbers.stream().reduce((t, n) -> t + n).get();
// System.out.println("Total: " + total);}
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);
//collect()方法的参数为一个java.util.stream.Collector类型对象,可以用java.util.stream.Collectors工具类提供的静态方法来生成,Collectors类实现很多的归约操作,如Collectors.toList()、Collectors.toSet()、Collectors.joining()(joining适用于字符串流)等。看一个简单示例:
//用map()方法生成新的流,再用collect()方法返回原数组的绝对值数组。
// public static void main(String[] args) {
// List numbers = Arrays.asList(-1, -2, 0, 4, 5);
// List abss = numbers.stream().map( n -> Math.abs(n)).collect(Collectors.toList());
// System.out.println("Abs list: " + abss);}
<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 minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
Optional<T> min(Comparator<? super T> comparator);
//返回流中最大值,列如年龄最大的人信息:
//Optional maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
Optional<T> max(Comparator<? super T> comparator);
//返回流中元素的总个数,列如:
//List numbers = Arrays.asList(-1, -2, 0, 4, 5);
//long count = numbers.parallelStream().filter(i -> i>0).count();
long count();
//检查是否至少匹配一个元素,列如:
boolean anyMatch(Predicate<? super T> predicate);
//检查是否匹配所有元素,列如是否都是成年人:
//boolean adult = personList.stream().allMatch(p -> p.getAge() >= 18);
boolean allMatch(Predicate<? super T> predicate);
//检查是否没有匹配所有元素,列如:
boolean noneMatch(Predicate<? super T> predicate);
//返回第一个元素,列如:
Optional<T> findFirst();
//返回当前流中的任意元素,列如:
Optional<T> findAny();
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));
}
public interface Builder<T> extends Consumer<T> {
@Override
void accept(T t);
default Builder<T> add(T t) {
accept(t);
return this;
}
Stream<T> build();
}
}
//常见的中间操作方法(filter、limit、skip、distinct、map、flatMap、sorted、peek)
import lombok.Data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestStreamAPI {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("向天笑",20,"中国",'M'));
personList.add(new Person("李康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));
// 1)找到年龄大于18岁的人并输出;
personList.stream().filter((p) -> p.getAge() > 18).forEach(System.out::println);
System.out.println("-------------------------------------------");
// 2)找出所有中国人的数量
long chinaPersonNum = personList.stream().filter((p) -> p.getCountry().equals("中国")).count();
System.out.println("中国人有:" + chinaPersonNum);
// limit
personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);
System.out.println();
// skip
personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);
// distinct
personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);
// map
personList.stream().map((p) -> {
PersonCountry personName = new PersonCountry();
personName.setCountry(p.getCountry());
return personName;
}).distinct().forEach(System.out::println);
// map2
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","ddd");
final Stream<Stream<Character>> streamStream
= list.stream().map(TestStreamAPI::getCharacterByString);
// streamStream.forEach(System.out::println);
streamStream.forEach(sm -> sm.forEach(System.out::print));
// flatMap
final Stream<Character> characterStream = list.stream().flatMap(TestStreamAPI::getCharacterByString);
characterStream.forEach(System.out::print);
// sort
final Stream<Person> sorted = personList.stream().sorted((p1, p2) -> {
if (p1.getAge().equals(p2.getAge())) {
return p1.getName().compareTo(p2.getName());
} else {
return p1.getAge().compareTo(p2.getAge());
}
});
sorted.forEach(System.out::println);
// allMatch
final Stream<Person> stream = personList.stream();
final boolean adult = stream.allMatch(p -> p.getAge() >= 18);
System.out.println("是否都是成年人:" + adult);
final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
System.out.println("是否都是中国人:" + chinaese);
// max min
final Optional<Person> maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
System.out.println("年龄最大的人信息:" + maxAge.get());
final Optional<Person> minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
System.out.println("年龄最小的人信息:" + minAge.get());
// reduce
List<Integer> integerList = new ArrayList<>(100);
for(int i = 1;i <= 100;i++) {
integerList.add(i);
}
final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
System.out.println("结果为:" + reduce);
final Optional<Integer> totalAge = personList.stream().map(Person::getAge).reduce(Integer::sum);
System.out.println("年龄总和:" + totalAge);
// collect
final List<String> collect = personList.stream().map(p -> p.getCountry()).distinct().collect(Collectors.toList());
System.out.println(collect);
final Double collect1 = personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
System.out.println("平均年龄为:" + collect1);
final Optional<Integer> maxAge2 = personList.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compareTo));
System.out.println(maxAge2.get());
try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
final Optional<Integer> minAge2 = integerStream.collect(Collectors.minBy(Integer::compareTo));
System.out.println(minAge2.get());
}
}
public static Stream<Character> getCharacterByString(String str) {
List<Character> characterList = new ArrayList<>();
for (Character character : str.toCharArray()) {
characterList.add(character);
}
return characterList.stream();
}
}
@Data
class PersonCountry {
private String country;
}
@Data
class Person {
private String name;
private Integer age;
private String country;
private char sex;
public Person(String name, Integer age, String country, char sex) {
this.name = name;
this.age = age;
this.country = country;
this.sex = sex;
}
}