Javaでストリームの流れ
ストリームを言えば、それは規定の人、実際には、I / Oストリームを考えることは容易である「フロー」、それが意味する「IOストリーム」でなければなりませんか?でJava 8では、ラムダは、の導入によってもたらされた関数型プログラミングのおかげストリームの新しい概念は、既存のライブラリのコレクションの欠点を解決するために使用されてきました。
I.はじめに
コードセットを介して、従来の多段階
ほとんどすべてのセット(例えば、のCollection
インタフェース又はMap
インタフェース、等)を直接または間接的にトラバース動作をサポートしています。私たちは、追加、削除、外取得するために必要なことに加えて、コレクションの要素を操作する必要がある場合や、典型的には、トラバーサルのコレクションがあります。
欠点ループスルー
Javaのラムダ8は、我々は上でより多くの焦点を合わせることができるように何をするかではなく、(何を)行う方法(どのように)、内部クラスのこの組み合わせは、すでに比較イラストを行っていました。今、我々は慎重に例のコードは、あなたが見つけることができるかを理解します:
- ループ構文について「で実行する方法」
- ループのループがある「何をしますか」
なぜサイクリング?トラバースさせるため。しかし、サイクリングには、それを通過する唯一の方法はありますか?トラバースは、個別に各要素を処理するための手段ではない最初のサイクルからの一連の処理の最後に。前者は方法です目標、です。
想像して、あなたはフィルタエレメントフィルタを設定する場合:
フィルタの条件に応じて設定Bのサブセット。
第2の条件は、次いで濾過するサブセットCを。
それをどのように行うには?実際には8前のJavaは次のようになります。
public class Demo01List { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰"); //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中 List<String> listA = new ArrayList<>(); for (String s : list) { if (s.startsWith("张")) { listA.add(s); } } //对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中 List<String> listB = new ArrayList<>(); for (String s : listA) { if (s.length() == 3) { listB.add(s); } } //遍历listB集合 for (String s : listB) { System.out.println(s); } } }
このコードは、3つのサイクル、異なる機能のそれぞれが含まれています。
- 張さんは、最初にすべての人をスクリーニング。
- そして、三つの言葉をスクリーニング人の名前があります。
- 結果がプリントアウトされた後。
私たちが動作する要素を設定する必要があるときは、常にサイクル、サイクル、リサイクルする必要があります。それはそれだけで自然なのですか?いいえ。サイクリングは、物事の道、終わりではないです。一方、線形トラバーサルサイクルを使用すると、一度だけということを意味します。あなたは再び横切り、その後、別のサイクルを使用する場合のみゼロから始めることができます。
これは、ラムダ誘導体ストリームは、何よりエレガントな言葉遣い、それを私たちにもたらすことができますか?
より好ましくは、文言をストリーミング
エレガントとは何を意味するのか援助Java8ストリームAPIで次の見て、:
public class Demo02Stream {
public static void main(String[] args) {
//创建一个List集合,存储姓名
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
//对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
//对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
//遍历listB集合
list.stream()
.filter(name -> name.startsWith("张"))
.filter(name -> name.length() == 3)
.forEach(name -> System.out.println(name));
}
}
論理的に独立の意味完全表示の文字通りの意味に直接コードを読み取る:取得ストリーム、張濾過、フィルタの長さは3、一つの印刷ものです。コードが通過する直鎖状、環状またはその他のアルゴリズムの使用を反映していない、私たちは本当に実行する必要があり、より良いコードの内容を反映しています。
第二に、ストリーミングの概要のアイデア
注:伝統的なIOストリームのステレオタイプを忘れてください!
全体的に、ストリーミングアイデアは、「工場の床に似ている生産ライン。」
複数の要素が動作するための必要性(特にマルチステップ動作)は、アカウントのパフォーマンスと利便性を考慮すると、我々は最初にすべての良い戦いの「モデル」ステッププログラム、そして、それを実行するためのプログラムに従ってください。
これは、プログラムがあるが、それは、要素のコレクションのプロセススキームである、複数の操作をカウントし、スキップ、図フィルタリング、マッピングに示されている「機能モデル」。図は、各ブロックは、「フロー」、メソッドは、指定された呼び出しであり、別のフローは、モデル変換からフローモデルであってもよいです。図3は、右側の番号と最終的な結果です。
ここではfilter 、 map 、 skip
関数モデルの動作であり、要素のセットは、実際に処理されていません。この方法の終わり場合にのみcount
実行されると、全体の動作は、モデル指定されたポリシーに従って実行されるであろう。ラムダ特性の実現の遅れのおかげ。
注:「ストリームフローは、」実際にモデル関数の要素の集合であり、それが設定されていない、データ構造ではなく、それ自体で任意の要素(またはアドレス値)を記憶していません。
流れ(フロー)は、データ・ソース・キューからの要素であります
- 要素は、キューが形成され、オブジェクトの特定のタイプです。Javaの流れの中や要素を格納しませんが、オンデマンド・コンピューティング。
- 数据源 流的来源。 可以是集合,数组 等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭 代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。
当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)→ 数据转换→执行操作获取想要的结 果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以 像链条一样排列,变成一个管道。
三、获取流
java.util.stream.Stream<T>
是Java 8新加入的常用的流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下几种常用的方式:
所有的
Collection
集合都可以通过stream
默认方法获取流;Stream
接口的静态方法of
可以获取数组对应的流。
根据Collection & Map & Array 获取流 &
首先, java.util.Collection
接口中加入了default方法 stream
用来获取流,所以其所有实现类均可获取流。
java.util.Map
接口不是 Collection
的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流 需要分key、value或entry等情况:
如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream
接口中提供了静态方法 of
,使用很简单:
备注:
of
方法的参数其实是一个可变参数,所以支持数组。
/*
java.util.stream.Stream<T>是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)
获取一个流非常简单,有以下几种常用的方式:
- 所有的Collection集合都可以通过stream默认方法获取流;
default Stream<E> stream()
- Stream接口的静态方法of可以获取数组对应的流。
static <T> Stream<T> of(T... values)
参数是一个可变参数,那么我们就可以传递一个数组
*/
public class Demo01GetStream {
public static void main(String[] args) {
//把集合转换为Stream流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Map<String,String> map = new HashMap<>();
//获取键,存储到一个Set集合中
Set<String> keySet = map.keySet();
Stream<String> stream3 = keySet.stream();
//获取值,存储到一个Collection集合中
Collection<String> values = map.values();
Stream<String> stream4 = values.stream();
//获取键值对(键与值的映射关系 entrySet)
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream5 = entries.stream();
//把数组转换为Stream流
Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5);
//可变参数可以传递数组
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream7 = Stream.of(arr);
String[] arr2 = {"a","bb","ccc"};
Stream<String> stream8 = Stream.of(arr2);
}
}
四、常用方法
流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
- 延迟方法:返回值类型仍然是
Stream
接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方 法均为延迟方法。) - 终结方法:返回值类型不再是
Stream
接口自身类型的方法,因此不再支持类似StringBuilder
那样的链式调 用。本小节中,终结方法包括count
和forEach
方法。
备注:本小节之外的更多方法,请自行参考API文档。
逐一处理:forEach
虽然方法名字叫 forEach
,但是与for循环中的“for-each”昵称不同。
void forEach(Consumer<? super T> action);
该方法接收一个 Consumer
接口函数,会将每一个流元素交给该函数进行处理。
基本使用:
public class Demo02Stream_forEach {
public static void main(String[] args) {
//获取一个Stream流
Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六", "田七");
stream.forEach(name->System.out.println(name));
}
}
过滤:filter
可以通过 filter
方法将一个流转换成另一个子集流。方法签名:
Stream<T> filter(Predicate<? super T> predicate);
该接口接收一个 Predicate
函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件
基本使用:
/*
Stream流中的常用方法_filter:用于对Stream流中的数据进行过滤
Stream<T> filter(Predicate<? super T> predicate);
filter方法的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤
Predicate中的抽象方法:
boolean test(T t);
*/
public class Demo03Stream_filter {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
//对Stream流中的元素进行过滤,只要姓张的人
Stream<String> stream2 = stream.filter((String name)->{return name.startsWith("张");});
//遍历stream2流
stream2.forEach(name-> System.out.println(name));
/*
Stream流属于管道流,只能被消费(使用)一次
第一个Stream流调用完毕方法,数据就会流转到下一个Stream上
而这时第一个Stream流已经使用完毕,就会关闭了
所以第一个Stream流就不能再调用方法了
IllegalStateException: stream has already been operated upon or closed
*/
//遍历stream流
stream.forEach(name-> System.out.println(name));
}
}
映射:map
如果需要将流中的元素映射到另一个流中,可以使用 map
方法。方法签名:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个 Function
函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流,称为映射。
基本使用
public class Demo04Stream_map {
public static void main(String[] args) {
//获取一个String类型的Stream流
Stream<String> stream = Stream.of("1", "2", "3", "4");
//使用map方法,把字符串类型的整数,转换(映射)为Integer类型的整数
Stream<Integer> stream2 = stream.map((String s)->{
return Integer.parseInt(s);
});
//遍历Stream2流
stream2.forEach(i-> System.out.println(i));
}
}
统计个数:count
正如旧集合 Collection
当中的 size
方法一样,流提供 count
方法来数一数其中的元素个数:
long count();
该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。
基本使用:
public class Demo05Stream_count {
public static void main(String[] args) {
//获取一个Stream流
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
Stream<Integer> stream = list.stream();
long count = stream.count();
System.out.println(count);//输出7
}
取用前几个:limit
`limit方法可以对流进行截取,只取用前n个,方法签名:
Stream<T> limit(long maxSize);
基本使用:
public class Demo06Stream_limit {
public static void main(String[] args) {
//获取一个Stream流
String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
Stream<String> stream = Stream.of(arr);
//使用limit对Stream流中的元素进行截取,只要前3个元素
Stream<String> stream2 = stream.limit(3);
//遍历stream2流
stream2.forEach(name-> System.out.println(name));
}
}
跳过前几个:skip
如果希望跳过前几个元素,可以使用 skip
方法获取一个截取之后的新流:
Stream<T> skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
基本使用:
public class Demo07Stream_skip {
public static void main(String[] args) {
//获取一个Stream流
String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
Stream<String> stream = Stream.of(arr);
//使用skip方法跳过前3个元素
Stream<String> stream2 = stream.skip(3);
//遍历stream2流
stream2.forEach(name-> System.out.println(name));
}
}
// 灰太狼
// 红太狼
组合:concat
如果有两个流,希望合并成为一个流,那么可以使用 Stream
接口的静态方法 concat
:
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 class Demo08Stream_concat {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream1 = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
//获取一个Stream流
String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
Stream<String> stream2 = Stream.of(arr);
//把以上两个流组合为一个流
Stream<String> concat = Stream.concat(stream1, stream2);
//遍历concat流
concat.forEach(name-> System.out.println(name));
}
}