Java流(Stream)操作實例-篩選、映射、查找匹配


准備工作

構建一個測試類,通過測試類先初始化一個數據源,具體如下。

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方法輸出的流的類型就是Stream⁢String>。

我們來再看一個例子,我們把方法引用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 類(java.util.Optional)是一個容器類,代表一個值存在或不存在。Java 8的庫設計人員引入了Optional ,這樣就不用返回眾所周知容易出問題的null了。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));


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM