Java 8 Stream API
Posted 爱coding的卖油翁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 8 Stream API相关的知识,希望对你有一定的参考价值。
1.创建Stream
流(Stream) 到底是什么呢?是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合讲的是数据,流讲的是计算。Stream遵循“做什么,而不是怎么去做”的原则。
Stream与集合的区别:
- Stream 自己不会存储元素。元素可能被存储在底层的集合中,或者根据需要产生出来。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream操作的三个步骤:
- 创建一个Stream。
一个数据源(如:集合、数组),获取一个流。 - 中间操作。
一个中间操作链,对数据源的数据进行处理。 - 终止操作。
一个终止操作,执行中间操作链,并产生结果。
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
- default Stream stream():返回一个顺序流。
- default Stream parallelStream():返回一个并行流。
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
- static Stream stream(T[] array):返回一个流。
- static IntStream stream(int[] array)
- static LongStream stream(long[] array)
- static DoubleStream stream(double[] array)
可以使用静态方法 Stream.of(),通过显示值创建一个流。它可以接收任意数量的参数。
- static Stream of(T… values) : 返回一个流。
可以使用静态方法 Stream.iterate()和Stream.generate(),创建无限流。
- static Stream iterate(final T seed, final UnaryOperator f)
- static Stream generate(Supplier s)
@Test
public void test1()
// 1. Collection 提供了两个方法 stream() 与 parallelStream()
List<String> list = new ArrayList<>();
// 获取一个顺序流
Stream<String> stream = list.stream();
// 获取一个并行流
Stream<String> parallelStream = list.parallelStream();
// 2. 通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(nums);
// 3. 通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6);
// 4. 创建无限流
// 迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);
// 生成
Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);
2.Stream的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。而在终止操作时一次性全部处理,称为“惰性求值”。
1.筛选与切片
filter(Predicate p):接收 Lambda , 从流中排除某些元素。
distinct():筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素。
limit(long maxSize):截断流,使其元素不超过给定数量。
skip(long n):跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补。
2.映射
map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
3.排序
sorted() :产生一个新流,其中按自然顺序排序。
sorted(Comparator comp):产生一个新流,其中按比较器顺序排序。
3.Stream的终止操作
终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
1.查找与匹配
allMatch(Predicate p):检查是否匹配所有元素。
anyMatch(Predicate p):检查是否至少匹配一个元素。
noneMatch(Predicate p):检查是否没有匹配所有元素。
findFirst():返回第一个元素。
findAny():返回当前流中的任意元素。
count():返回流中元素总数。
max(Comparator c):返回流中最大值。
min(Comparator c):返回流中最小值。
forEach(Consumer c):内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)。
2.归约(也叫聚合操作)
reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional。
3.收集结果
collect(Collector c):将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法。
Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下。
toList:把流中元素收集到List。
List< Employee > emps = list.stream().collect(Collectors.toList());
toSet:把流中元素收集到Set。
Set< Employee > emps = list.stream().collect(Collectors.toSet());
toCollection:把流中元素收集到创建的集合。
ArrayList< Employee > emps = list.stream().collect(Collectors.toCollection(ArrayList::new));
counting:计算流中元素的个数。
long count = list.stream().collect(Collectors.counting());
summingInt:对流中元素的整数属性求和。
int total = list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt:计算流中元素Integer属性的平均值。
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizingInt:收集流中Integer属性的统计值。如:平均值。
IntSummaryStatistics iss = list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining:连接流中每个字符串。
String str = list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy:根据比较器选择最大值。
Optional< Emp > max = list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minBy:根据比较器选择最小值。
Optional< Emp > min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducing:从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值。
int total = list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThen:包裹另一个收集器,对其结果转换函数。
inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));groupingBy:根据某属性值对流分组,属性为K,结果为V。
Map< Emp.Status, List< Emp>> map = list.stream().collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy:根据true或false进行分区。
Map< Boolean, List< Emp >> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));
// Test
public class Trader
private String name;
private String city;
public String getName()
return name;
public void setName(String name)
this.name = name;
public String getCity()
return city;
public void setCity(String city)
this.city = city;
public Trader(String name, String city)
super();
this.name = name;
this.city = city;
public Trader()
super();
@Override
public String toString()
return "Trader [name=" + name + ", city=" + city + "]";
public class Transaction
private Trader trader;
private int year;
private int value;
public Transaction()
public Transaction(Trader trader, int year, int value)
this.trader = trader;
this.year = year;
this.value = value;
public Trader getTrader()
return trader;
public void setTrader(Trader trader)
this.trader = trader;
public int getYear()
return year;
public void setYear(int year)
this.year = year;
public int getValue()
return value;
public void setValue(int value)
this.value = value;
@Override
public String toString()
return "Transaction [trader=" + trader + ", year=" + year + ", value=" + value + "]";
public class TestTransaction
List<Transaction> transactions = null;
@Before
public void before()
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");
transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
/**
* 1. 找出2011年发生的所有交易, 并按交易额排序(从低到高)
*/
@Test
public void test1()
transactions.stream()
.filter((t) -> t.getYear() == 2011)
.sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
.forEach(System.out::println);
/**
* 2. 交易员都在哪些不同的城市工作过?
*/
@Test
public void test2()
transactions.stream()
.map((t) -> t.getTrader().getCity())
.distinct()
.forEach(System.out::println);
/**
* 3. 查找所有来自剑桥的交易员,并按姓名排序
*/
@Test
public void test3()
transactions.stream()
.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
.sorted((t1, t2) -> t1.getTrader().getName().compareTo(t2.getTrader().getName()))
.forEach(System.out::println);
/**
* 4. 返回所有交易员的姓名字符串,按字母顺序排序
*/
@Test
public void test4()
transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.forEach(System.out::println);
System.out.println("--------------------------");
String reduce = transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.reduce("", String::concat);
System.out.println(reduce);
System.out.println("--------------------------");
transactions.stream()
.map((t) -> t.getTrader().getName())
.flatMap(TestTransaction::filterCharacter)
.sorted((t1, t2) -> t1.compareToIgnoreCase(t2))
.forEach(System.out::println);
public static Stream<String> filterCharacter(String str)
List<String> list = new ArrayList<>();
for (Character ch : str.toCharArray())
list.add(ch.toString());
return list.stream();
/**
* 5. 有没有交易员是在米兰工作的?
*/
@Test
public void test5()
boolean b = transactions.stream()
.anyMatch((t) -> t.getTrader().getCity().equals("Milan"));
System.out.println(b);
/**
* 6. 打印生活在剑桥的交易员的所有交易额
*/
@Test
public void test6()
Optional<Integer> optional = transactions.stream()
.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
.map((t) -> t.getValue())
.reduce(Integer::sum);
System.out.println(optional.get());
/**
* 7. 所有交易中,最高的交易额是多少
*/
@Test
public void test7()
Optional<Integer> max = transactions.stream()
.map((t) -> t.getValue())
.max(Integer::compareTo);
System.out.println(max.get());
/**
* 8. 找到交易额最小的交易
*/
@Test
public void test8()
Optional<Transaction> min = transactions.stream()
.min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
System.out.println(min.get());
将聚合的结果收集到Map中,可以使用Collections.toMap()方法,有2个参数,分别用来生产map的key和value。(java.util.stream.Collectors)如果有多个元素拥有相同的key,则会抛出一个IllegalStateException异常。
@Test
public void test1()
Map<Integer, String> map = emps.stream()
.collect(Collectors.toMap(Employee::getId, Employee::getName));
for(Integer i : map.keySet())
System.out.println(map.get(i));
@Test
public void test2()
Map<Integer, Employee> map = emps.stream()
.collect(Collectors.toMap(Employee::getId, Function.identity()));
for(Integer i : map.keySet())
System.out.println(map.get(i));
对于toMap方法的每种形式,都有一个对应的toConcurrentMap方法,用来产生一个并发的Stream。在并行收集的过程中,应当只使用一个并发的map。一个共享的map要比合并的map效率更高。当然,使用共享的map无法得到有序的结果。
4.原始信息流
对于Stream< Integer >、Stream< Long >、Stream< Double >这样的流,不过将每个数包装成相应的对象显然是一个低效的做法,Stream API提供了IntStream、LongStream、DoubleStream等类型,专门用来存储原始类型值。
IntStream intStream = IntStream.of(1,2,3,4,5,6);
DoubleStream doubleStream = DoubleStream.of(1f,2f,3f,4f);
LongStream longStream = LongStream.of(1l,2l,3l,4l,5l,6l);
Arrays.stream(value, from, to);
IntStream和LongStream有静态方法range()和rangeClosed(),用来产生一个范围内的整数。range(0, 100)和rangeClosed(0, 100),前者不包括上限,后者包括上限。
将对象刘转化为原始类型流,通过mapToLong、mapToInt、mapToDouble方法。
Stream<String> words = ...;
IntStream intStream = words.mapToInt(String::length);
将原始类型流转化为对象流,通过boxed方法。
Stream<Integer> stream = IntStream.range(0, 100).boxed();
注意:Random类现在提供了ints、longs、doubles这些方法,用来返回包含随机数的原始类型流。
5.Optional< T >类
Optional< T > 类(java.util.Optional) 是一个容器类,Optional< T >对象或者是对一个T类型对象的封装,或者表示不是任何对象。它比一般指向T类型的引用更安全,因为它不会返回null,可以避免空指针异常。
它的实现代码很少,只有350行,下面就对一些常用方法进行介绍:
// 只有2个成员变量
private static final Optional< ? > EMPTY = new Optional<>();
private final T value;
- Optional.of(T t):创建一个 Optional 实例。
public static <T> Optional<T> of(T value)
return new Optional<>(value);
- Optional.empty():创建一个空的 Optional 实例。
public static<T> Optional<T> empty()
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
- Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例。
public static <T> Optional<T> ofNullable(T value)
return value == null ? empty() : of(value);
- get():如果对象存在,会返回该对象,否则会抛出一个NoSuchElementException异常。
public T get()
if (value == null)
throw new NoSuchElementException("No value present");
return value;
- isPresent():判断是否包含值。
public boolean isPresent()
return value != null;
- orElse(T t):如果调用对象包含值,返回该值,否则返回t。
public T orElse(T other)
return value != null ? value : other;
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值。
public T orElseGet(Supplier<? extends T> other)
return value != null ? value : other.get();
`
- map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()。
public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else
return Optional.ofNullable(mapper.apply(value));
- flatMap(Function mapper):与 map 类似,要求返回值必须是Optional。
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else
return Objects.requireNonNull(mapper.apply(value));
使用Optional可以避免空指针异常,高效的使用Optional的关键在于,使用一个或者接受正确值、或者返回另一个替代值的方法。以前使用 !=null判断, 现在使用Optional.isPresent()判断。
@Test
public void test2()
Optional<Employee> optional = Optional.empty();
if (optional.isPresent())
System.out.println(optional.get());
else
System.out.println("optional is null");
// 如果optional中没有该值,就返回一个默认值。
@Test
public void test3()
Optional<Employee> optional = Optional.empty();
Employee employee = optional.orElse(new Employee());
System.out.println(employee);
6.并行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。默认情况下,流操作会创建一个串行流。Collection.parallelStream()可以创建一个并行流,parallel()方法可以将任意的串行流转换为一个并行流。
Stream< String > parallelWords = Stream.of(words).parallel();
记住,流不会收集它自己的数据,这些数据总是存在与另一个集合之中。如果你想要修改原有的集合,吗、、那么就无法定义流操作的输出。
// ok
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
list.add("hello");
long count = stream.distinct().count();
// error
List<String> list2 = new ArrayList<>();
Stream<String> stream2 = list2.stream();
stream2.forEach(s ->
if (s.length() < 12)
list2.remove(s);
);
关于并行的操作,还在学习中,不是很了解…
以上是关于Java 8 Stream API的主要内容,如果未能解决你的问题,请参考以下文章