Stream流
JDK1.8的新特性之一
它跟我們所學的 IO 流完全沒有什麼關係
Stream 它是Lambda的衍生物。
通過一個例子來看:
需求:我們先找出性張的,再性張的中找出三個字的,然後遍歷集合
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList();
//給集合添加元素
Collections.addAll(list,"張無忌","張三","王二","張三明","馬六");
//先找姓張的
ArrayList<String> zhangList = new ArrayList<>();
for (String s : list) {
if(s.startsWith("張")){
zhangList.add(s);
}
}
//在找三個字的
ArrayList<String> threeList = new ArrayList<>();
for (String s : zhangList){
if (s.length() == 3){
threeList.add(s);
}
}
//遍歷集合
for (String s : threeList){
System.out.println(s);
}
}
}
結果:
張無忌
張三明
這樣寫顯然我們要進行三次遍歷,不但代碼多,而且還很浪費功夫,還容易寫錯。
JDK1.8的新特性中就指明瞭流的思想,下面讓我們看看使用Stream之後的代碼吧。
ArrayList<String> list = new ArrayList();
//給集合添加元素
Collections.addAll(list,"張無忌","張三","王二","張三明","馬六");
//使用Stream流
list.stream().filter(s->s.startsWith("張"))
.filter(s->s.length() == 3).forEach(s-> System.out.println(s));
結果:
張無忌
張三明
可以看到兩個例子的結果完全一致,流的使用簡化了很多的代碼。
我相信正在讀文章的你也很好奇,那麼久讓我們先看一下流是怎樣做到這一點的吧。
下面的代碼片段是流的傳統寫法:
我們可以發現,stream流的方法中都有一個參數,而且該參數都是一個接口並且該接口中只有一個抽象方法,我相信看過我上篇Lambda表達式的小夥伴都應該知道,這是符合Lambda表達式的使用條件的,那麼就讓我們來簡化一下吧。
//流的原始方式寫法
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("張");
}
}).filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() == 3;
}
}).forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//流與Lambda表達式結合使用,簡化後的代碼如下:
list.stream().filter(s->s.startsWith("張"))
.filter(s->s.length() == 3).forEach(s-> System.out.println(s));
從這裏可以看到,流的引入給我們帶來了很大的便利,下面就讓我們充分的認識一下它吧!
獲取流的方式
java.util.stream.Stream 是Java 8新加入的最常用的流接口。(這並不是一個函數式接口。)
獲取一個流非常簡單,有以下幾種常用的方式:
Collection 集合及其子類都可以通過 .stream() 獲取流。
Stream接口的靜態方法 .of 可以獲取數組對應的流。
//通過單列集合獲取流
ArrayList<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
//通過Stream接口的靜態方法也可以獲取流
String[] arr = {"a","b","c"};
Stream<String> Stream1 = Stream.of(arr);
//map方式獲取流 基本用不到
HashMap<String, String> map = new HashMap<>();
Stream<String> s1 = map.keySet().stream();
Stream<String> s2 = map.values().stream();
Stream<Map.Entry<String, String>> s3 = map.entrySet().stream();
常用方法
流模型的操作很豐富,這裏介紹一些常用的API。這些方法可以被分成兩種:
- 終結方法:返回值類型不再是 Stream 接口自身類型的方法,因此不再支持類似 StringBuilder 那樣鏈式調用。本次介紹的終結方法包括 count 和 forEach 方法。
- 非終結方法:返回值類型仍然是 Stream 接口自身類型的方法,因此支持鏈式調用。(除了終結方法外,其餘方法均爲非終結方法。)
方法介紹:
終結方法:不支持鏈式調用
- count : 統計個數(終結方法,不支持鏈式調用)
- forEach :逐一處理,遍歷的意思(終結方法,不支持鏈式調用)
其他方法(非終結方法):除了終結方法,其他方法都支持鏈式調用
- filter : 過濾
- limit : 取前幾個
- skip : 跳過前幾個
- map : 映射
- static concat : 組合,拼接(只能通過Stream接口調用)
- static of : 獲取數組對應的流(只能通過Stream接口調用)
- collect(Collectors接口) : 把流轉換成指定的集合
- toArray : 轉化成數組
filter與forEach使用:
public class Work5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"王佳樂","張三丰","王思聰","張飛","劉曉敏","張靚穎","王敏");
//獲取流
Stream<String> stream = list.stream();
//注意:因爲已經調用了終結方法,後面就不能使用這個流進行操作了,因爲該流已經不存在了,要想操作還需要再次創建新的流
stream.filter(s->s.startsWith("張")).forEach(s-> System.out.println(s));
//流對象已經消失,該語句會報錯的
stream.filter(s -> s.startsWith("王")).forEach(s-> System.out.println(s));
}
}
結果:
注意:因爲已經調用了終結方法,後面就不能使用這個流進行操作了,因爲該流已經不存在了,要想操作還需要再次創建新的流
count使用:
public class Test1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("張無忌");
list.add("張三");
//count是終結方法,調用後此流就不能在使用,不然會報異常,要想使用需要新創建一個流
long count = list.stream().count();
System.out.println(count);
}
}
結果:
2
limit使用:
public class Work5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//給集合添加元素
Collections.addAll(list,"王佳樂","張三丰","王思聰","張飛","劉曉敏","張靚穎","王敏");
//獲取流
Stream<String> stream = list.stream();
//調用方法 使用limit獲取集合前三個並打印
stream.limit(3).forEach(s-> System.out.println(s));
}
}
結果:
王佳樂
張三丰
王思聰
skip使用:
public class Work5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//給集合添加元素
Collections.addAll(list,"王佳樂","張三丰","王思聰","張飛","劉曉敏","張靚穎","王敏");
//獲取流
Stream<String> stream = list.stream();
//調用方法 使用skip跳過集合中前三個元素並打印
stream.skip(3).forEach(s-> System.out.println(s));
}
}
結果:
張飛
劉曉敏
張靚穎
王敏
map使用
//Person類
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
//測試類
public class Work6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"王佳樂","張三丰","王思聰","張飛");
//把集合中的元素映射到Person對象中並打印
list.stream().map(Person::new).forEach(s-> System.out.println(s));
}
}
結果:
Person{name='王佳樂'}
Person{name='張三丰'}
Person{name='王思聰'}
Person{name='張飛'}
collect(Collectors接口)使用
Collectors接口下的方法:
toList 、 toSet 等等
//Person類
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
//測試類
public class Work6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"王佳樂","張三丰","王思聰","張飛");
//把集合元素映射到Person對象中,並且把流轉換成list集合
List<Person> collect = list.stream().map(Person::new).collect(Collectors.toList());
//打印list集合
System.out.println(collect);
}
}
結果:
[Person{name='王佳樂'}, Person{name='張三丰'}, Person{name='王思聰'}, Person{name='張飛'}]
toArray使用:
public class Test1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("張無忌");
list.add("張三");
list.add("王二");
list.add("馬六");
list.add("馬騰飛");
//獲取流,找出姓張的,然後把流轉換成數組
Object[] arr = list.stream().filter(s -> s.startsWith("張")).toArray();
System.out.println(Arrays.toString(arr));
}
}
結果:
[張無忌, 張三]
concat使用:
注意:concat是靜態方法,要使用Stream接口來調用
public class Test1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("張無忌");
list.add("張三");
ArrayList<String> list1 = new ArrayList<>();
list1.add("王二");
list1.add("馬六");
list1.add("馬騰飛");
//獲取兩個集合的流對象
Stream<String> stream = list.stream();
Stream<String> stream1 = list1.stream();
//調用 靜態的concat方法
Stream<String> concat = Stream.concat(stream, stream1);
//打印 concat流
concat.forEach(s-> System.out.println(s));
}
}
結果:
張無忌
張三
王二
馬六
馬騰飛
of使用:
注意:of是靜態方法,要使用Stream接口來調用
//通過Stream接口的靜態方法也可以獲取流
String[] arr = {"a","b","c"};
Stream<String> stream = Stream.of(arr);
如果能夠幫到你,就留下一個贊吧!