Java 8 Stream API
来源:互联网 发布:淘宝买高达模型 编辑:程序博客网 时间:2024/06/06 19:33
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));
// Testpublic 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
- Java 8 Stream API
- Java 8 Stream API详解
- JAVA 8 Stream API 对Stream进行分组、分区
- Java 8新特性:Stream API
- Java 8新特性:Stream API
- Java 8新特性 Stream API 编程
- Java 8——Stream API
- Java 8学习之Stream API
- Java 8: Stream API 快速入门
- Java 8 – Stream API 快速入门
- Java 8 Stream API 实用指南
- java stream api介绍
- java stream api
- java twitter stream api & mongoDB
- Java 8新特性:全新的Stream API
- JAVA SE 8 学习笔记(二)Stream API
- java 8 新特性(4.Stream API)
- 机房重构——基本数据设定
- JAVA面向对象
- 函数的数据传递
- SSM杰信商务平台项目day10
- ARMlcd的简单使用
- Java 8 Stream API
- django学习01
- poj 2411 状态压缩dp
- mongodb,redis,mysql 简要对比
- Discovering Permutations LightOJ
- Java学习路线
- CAN网络中的电阻
- ZOJ 142
- Android中创建一个商品展示小案例