准備工作
構建一個測試類,通過測試類先初始化一個數據源,具體如下。
public class TestObject {
private String name;
private String sex;
private int age;
private String email;
private boolean isMng;
public TestObject() {
}
public TestObject(String name,String sex,int age,String email,boolean isMng){
this.name=name;
this.sex=sex;
this.age=age;
this.email=email;
this.isMng=isMng;
}
//... ...get、set方法就不貼出來了,多又麻煩
}
在測試類中定義初始化數據源
public class StreamOperation {
static List<TestObject> list = Arrays.asList(
new TestObject("Ron","M",10,"ron.zheng@tfschange.com",false),
new TestObject("KDS","W",10,"kds@qq.com",false),
new TestObject("BoDuo","W",30,"boduo@163.com",false),
new TestObject("CangJin","W",10,"cangjin@gmail.com",false),
new TestObject("XiaoZe","W",30,"xiaoze@hotmail.com",true),
new TestObject("James","M",10,"leblonjames@hotmail.com",true),
new TestObject("Allen","M",50,"allen.lei@tfschange.com",true),
new TestObject("Smith","M",10,"jr.smith@cel.com",true),
new TestObject("Wade","M",20,"dw.wade@cel.com",true),
new TestObject("Wade","M",20,"dw.wade@cel.com",false)
);
}
用謂詞篩選
Streams接口支持filter方法,該操作會接受一個謂詞(一個返回boolean的函數)作為參數,並返回一個包括所有符合謂詞的元素的流。比如我們需要篩選isMng為ture的數據並打印名字就可以按照如下的方式處理。
/**
* @Comment 獲取Leader
* @Author Ron
* @Date 2017年11月24日 下午2:01:16
* @return
*/
public static List<TestObject> getLeader() {
return list.stream().filter(TestObject::isMng).collect(Collectors.toList());
}
public static void main(String[] args) {
List<TestObject> leaders = getLeader();
leaders.stream().forEach(leader->System.out.println(leader.getName()));
}
篩選各異的元素
流還支持一個叫作distinct的方法,它會返回一個元素各異(根據流所生成元素的hashCode和equals方法實現)的流。例如,以下代碼會篩選出列表中所有的偶數,並確保沒有重復。
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println);
}
截短流
流支持limit(n)方法,該方法會返回一個不超過給定長度的流。所需的長度作為參數傳遞給limit。如果流是有序的,則最多會返回前n個元素。比如選出前5個sex為M的對象並打印其名稱可以按如下的代碼操作。
list.stream().filter(u->u.getSex().equals("M")).limit(5).forEach(u->System.out.println(u.getName()));
如果我們需要選出前5個sex為M的對象並按照名稱排序之后打印其名稱可以按如下的代碼操作。
list.stream()
.filter(u->u.getSex().equals("M"))
.limit(5)
.sorted(Comparator.comparing(TestObject::getName))
.forEach(u->System.out.println(u.getName()));
跳過元素
流還支持skip(n)方法,返回一個扔掉了前n個元素的流。如果流中元素不足n個,則返回一個空流。請注意, limit(n)和skip(n)是互補的。例如,下面的代碼將會跳過篩選出來的第一個元素並打印名字。
list.stream()
.filter(u->u.getSex().equals("M"))
.sorted(Comparator.comparing(TestObject::getName))
.skip(1)
.forEach(u->System.out.println(u.getName()));
對流中每一個元素應用函數
流支持map方法,它會接受一個函數作為參數。這個函數會被應用到每個元素上,並將其映射成一個新的元素(使用映射一詞,是因為它和轉換類似,但其中的細微差別在於它是“創建一個新版本”而不是去“修改”)。例如,下面的代碼把方法引用TestObject::getName傳給了map方法,來提取流中用戶的名稱並打印:
list.stream()
.map(TestObject::getName)
.collect(Collectors.toList())
.forEach(System.out::println);
因為getName方法返回一個String,所以map方法輸出的流的類型就是StreamString>。
我們來再看一個例子,我們把方法引用TestObject::getName傳給了map方法,來提取流中用戶的名稱,然后再打印用戶名稱的長度。你可以像下面這樣,給map傳遞一個方法引用String::length來解決這個問題:
list.stream()
.map(TestObject::getName)
.map(String::length)
.collect(Collectors.toList())
.forEach(System.out::println);
檢查謂詞是否至少匹配一個元素
anyMatch方法可以回答“流中是否有一個元素能匹配給定的謂詞”。比如,你可以用它來看看用戶列表里面是否有名稱為Ron的對象可選擇:
if(list.stream().anyMatch(u->u.getName().equals("Ron"))){
System.out.println("Ron已經到了");
}
anyMatch方法返回一個boolean,因此是一個終端操作。
檢查謂詞是否匹配所有元素
allMatch方法的工作原理和anyMatch類似,但它會看看流中的元素是否都能匹配給定的謂詞。比如,你可以用它來看看用戶是否都大於10歲。
if(list.stream().allMatch(u->u.getAge()>=10)){
System.out.println("很棒,都大於10歲");
}else{
System.out.println("原來都還沒發育");
}
和allMatch相對的是noneMatch。它可以確保流中沒有任何元素與給定的謂詞匹配。比如,你可以用noneMatch重寫前面的例子:
if(list.stream().noneMatch(u->u.getAge()<10)){
System.out.println("很棒,都大於10歲");
}else{
System.out.println("原來都還沒發育");
}
anyMatch、 allMatch和noneMatch這三個操作都用到了我們所謂的短路,這就是大家熟悉的Java中&&和||運算符短路在流中的版本。
Optional簡介
Optional
isPresent()將在Optional包含值的時候返回true, 否則返回false。
ifPresent(Consumer block)會在值存在的時候執行給定的代碼塊。
T get()會在值存在時返回值,否則拋出一個NoSuchElement異常。
T orElse(T other)會在值存在時返回值,否則返回一個默認值。
查找元素
findAny方法將返回當前流中的任意元素。它可以與其他流操作結合使用。
例如,我們需要顯示的檢查是否存在一個名為‘Ron’的人並顯示其名稱就可以按照如下的代碼操作。
list.stream()
.filter(u->u.getName().equals("Ron"))
.findAny()
.ifPresent(u->System.out.println(u.getName()));
流水線將在后台進行優化使其只需走一遍,並在利用短路找到結果時立即結束。
查找第一個元素
有些流有一個出現順序(encounter order)來指定流中項目出現的邏輯順序(比如由List或排序好的數據列生成的流)。對於這種流,你可能想要找到第一個元素。為此有一個findFirst方法,它的工作方式類似於findany。
例如我們需要找到第一個isLeader為ture的對象並打印其名字,就可以按照如下的代碼操作。
list.stream()
.filter(u->u.isLeader())
.findFirst()
.ifPresent(u->System.out.println(u.getName()));
何時使用findFirst和findAny
你可能會想,為什么會同時有findFirst和findAny呢?答案是並行。找到第一個元素在並行上限制更多。如果你不關心返回的元素是哪個,請使用findAny,因為它在使用並行流時限制較少。
將數據收集進一個列表(Stream 轉換為 List,允許重復值,有順序)
//1.將數據收集進一個列表(Stream 轉換為 List,允許重復值,有順序)
//創建流
Stream<String> language = Stream.of("java", "python", "C++","php","java");
List<String> listResult = language.collect(Collectors.toList());
result.forEach(System.out::println);
Set
Set<String> setResult = language.collect(Collectors.toSet());
用自定義的實現Collection的數據結構收集
List<String> list = Arrays.asList("java", "python", "C++","php","java");
//用LinkedList收集
List<String> linkedListResult = list.stream().collect(Collectors.toCollection(LinkedList::new));
linkedListResult.forEach(System.out::println);
System.out.println("--------------");
//用CopyOnWriteArrayList收集
List<String> copyOnWriteArrayListResult = list.stream().collect(Collectors.toCollection(CopyOnWriteArrayList::new));
copyOnWriteArrayListResult.forEach(System.out::println);
System.out.println("--------------");
//用TreeSet收集
TreeSet<String> treeSetResult = list.stream().collect(Collectors.toCollection(TreeSet::new));
treeSetResult.forEach(System.out::println);
對Stream的字符串拼接
List<String> list = Arrays.asList("java", "python", "C++","php","java");
//直接將輸出結果拼接
System.out.println(list.stream().collect(Collectors.joining()));
//每個輸出結果之間加拼接符號“|”
System.out.println(list.stream().collect(Collectors.joining(" | ")));
//輸出結果開始頭為Start--,結尾為--End,中間用拼接符號“||”
System.out.println(list.stream().collect(Collectors.joining(" || ", "Start--", "--End")));
由一個list轉化成另一個list
// 利用stream進行類型轉化
List<String> stringList = new ArrayList<>();
stringList.add("a11");
stringList.add("b11");
stringList.add("c11");
stringList.add("d11");
stringList.add("e11");
List<Map<String,String>> stringList1 = stringList.stream().map(item->
{
Map<String,String> map = new HashMap<>();
map.put("name", item.toUpperCase());
return map;
}
).collect(Collectors.toList());
提取某一列(以name為例)
List<String> nameList = studentList.stream().map(StudentInfo::getName).collect(Collectors.toList());
//提取后輸出name
nameList.forEach(s-> System.out.println(s));
提取age列並排重(使用distinct()函數)
//從對象列表中提取age並排重
List<Integer> ageList = studentList.stream().map(StudentInfo::getAge).distinct().collect(Collectors.toList());
ageList.forEach(a-> System.out.println(a));