Java流库
Java中的流库是从Java 8中引入的,遵循“做什么而非怎么做”的原则。
与集合相比,流提供了一种可以让我们在更高的概念级别上指定计算任务的数据视图,它不存储其元素,操作不会修改其数据源同时尽可能惰性执行,这一位直至只需要其结果时操作才会执行。
操作流的典型流程包括三个阶段:
-
创建一个流。
-
指定将初始流转换为其他流的中间操作,可能包含多个步骤。
-
应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。此后这个流就不能用了。
流的创建
给定数据流的创建
1. 通过集合
Collection<E>的方法 | 说明 |
---|---|
default Stream<E> stream() | 产生当前集合中所有元素的顺序流 |
注:Map集合需要先调用entrySet()方法进行间接调用。
Collection list=new ArrayList<Integer>();
Map hashmap=new HashMap<String,Double>();
Stream<Integer> stream1=list.stream();
Stream<Map.Entry<String,Double>> stream2 = hashmap.entrySet().stream();
2. 通过数组
Arrays的方法 | 说明 |
---|---|
static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive) | 产生一个流,它的元素是由数组中指定范围内的元素构成的(参数startInclusive和endExclusive允许为空)。 |
String[] array=new String[]{"北京","上海","广州","深圳"};
Stream<String> stream3=Arrays.stream(array);
3. 通过零散数据
Stream的方法 | 说明 |
---|---|
static<T> Stream <T> of(T… values) | 产生一个元素为给定值的流。 |
Stream<Character> stream4=Stream.of('a','b','c','d');
空流的创建
Stream的方法 | 说明 |
---|---|
static<T> Stream <T> empty() | 产生一个不包含任何元素的流 |
Stream<String> stream5=Stream.empty();
无限流的创建
Stream方法 | 说明 |
---|---|
static<T> Stream<T> generate(Supplier<? extends T> s) | 产生一个无限流,它的值是通过反复调用函数s构建的。 |
static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) | 产生一个无限流,它的元素包含seed、在seed上调用f产生的值、在前一个元素上调用f产生的值,等等。() |
Stream<String> stream6=Stream.generate(()->"Echo");
Stream<BigInteger> stream7=Stream.iterate(BigInteger.ZERO,n->n.add(BigInteger.ONE));
常用的中间操作
过滤和映射
Stream方法 | 说明 |
---|---|
Stream<T> filter(Predicate<? super T> predicate) | 产生一个流,它包含当前流中所有满足谓词条件的元素 |
<R> Stream<R> map(Function<? super T, ? extends R> mapper) | 产生一个流,它包含将mapper应用于当前流中所有元素所产生的结果 |
ArrayList<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Stream<Integer> st1_1 = list.stream().filter(n->n>2); //结果为3 4
Stream<Integer> st1_2 = list.stream().map(n->n*2); //结果为2 4 6 8
抽取和组合
Stream方法 | 说明 |
---|---|
Stream<T> limit(long maxSize) | 产生一个流,其中包含了当前流中最初的maxSize个元素 |
Stream<T> skip(long n) | 产生一个流,它的元素包是当前流中除了前n个元素之外的所有元素 |
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) | 产生一个流,它的元素是a的元素后面跟着b的元素 |
Stream<Integer> a = list.stream().limit(3); //结果为1 2 3
Stream<Integer> b = list.stream().skip(1); //结果为2 3 4
Stream<Integer> con = Stream.concat(a, b); //结果为1 2 3 2 3 4
流对象如果被消费过了就不允许再次使用,如上述例子中使用了a和b进行合并,如果再调用流a或流b系统将会报错。
排序和去重
Stream方法 | 说明 |
---|---|
Stream<T> distinct() | 产生一个流,包含当前流中所有不同的元素 |
Stream<T> sorted() | 产生一个流,它的元素是当前流中所有元素按照顺序排列的 |
HashSet<Character> list2=new HashSet<>();
list2.add('H');
list2.add('a');
list2.add('i');
list2.add('N');
list2.add('a');
list2.add('n');
Stream<Character> st2_1 = list2.stream().distinct(); //结果为H a i N n
Stream<Character> st2_2 = list2.stream().sorted(); //结果为H N a a i n
常用终结操作
流的计数
Stream方法 | 说明 |
---|---|
long count | 产生当前流中元素的数量 |
long count = list2.stream().count();
System.out.println(count);
遍历元素(输出元素)
Stream方法 | 说明 |
---|---|
void forEach(Consumer<? super ? T > action) | 在流的每个元素上调用action |
//输出list2集合中前两个小写字母
list2.stream().
filter(c->c>='a'&&c<='z').
distinct().
limit(2).
forEach(c->System.out.println(c));
//为方便展示进行分行,实质上以上为一条代码
Stream流支持链式编程,中间方法调用完后生成新的Stream流可以继续使用,但应用终结操作后就不能再使用了。
收集结果
Stream方法 | 说明 |
---|---|
<R,A> R collect(Collector<? super T,A,R> collector | 使用给定的收集器来收集当前流中的元素,Collectors类有用于多种收集器的工厂方法 |
Collectors方法 | 说明 |
---|---|
static <T> Collector<T, ?, List<T>> toList() | 把元素收集到List集合中 |
static <T> Collector<T, ?, Set<T>> toSet() | 把元素收集到Set集合中 |
static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) | 把元素收集到Map集合中 |
//将list集合前两个元素存到新的List集合中
List<Integer> collect1 = list.stream().limit(2).collect(Collectors.toList());
//将list2集合去重后存到新的Set集合中
Set<Character> collect2 = list2.stream().distinct().collect(Collectors.toSet());
//将下列List集合中大于24岁的人存入新的Map集合中
ArrayList<String> nameAge=new ArrayList<>();
nameAge.add("张三,23");
nameAge.add("李四,24");
nameAge.add("王五,25");
nameAge.add("赵六,26");
Map<String, Integer> map = nameAge.stream().
filter(s -> Integer.parseInt(s.split(",")[1]) >= 24).
collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
System.out.println(map);
前面的函数式接口对象基本都是以lambda表达式的形式进行书写,也可以根据个人选用匿名类的表达形式,如toMap方法用后者可能更能增加程序可读性。
参考资料
-
《Java核心技术·卷Ⅱ 高级特性(原书第11版)》第1章 Java8的流库