你需要了解的快捷開發 Java8 stream的使用(一) stream對List集合進行查詢、統計等操作

目錄

Java 8 Stream

什麼是 Stream?

生成流

綜合實例

1.創建實體類和數據

2.查詢方法

2.1 forEach()

2.2 filter(T -> boolean)

2.3findAny() 和 findFirst()

2.4map(T -> R) 和 flatMap(T -> Stream)

2.5 distinct()

2.6 limit(long n) 和 skip(long n)

3.統計方法

3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)

3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long) 

3.3 counting() 和 count()

3.4 summingInt()、summingLong()、summingDouble()

3.5 averagingInt()、averagingLong()、averagingDouble()


你需要了解的快捷開發 Java8 stream的使用(二) stream對List集合進行分組、彙總、排序等操作

Java 8 Stream

Java 8 API添加了一個新的抽象稱爲流Stream,可以讓你以一種聲明的方式處理數據。

Stream 使用一種類似用 SQL 語句從數據庫查詢數據的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。

Stream API可以極大提高Java程序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。

這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。

元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。

什麼是 Stream?

Stream(流)是一個來自數據源的元素隊列並支持聚合操作

  • 元素是特定類型的對象,形成一個隊列。 Java中的Stream並不會存儲元素,而是按需計算。
  • 數據源 流的來源。 可以是集合,數組,I/O channel, 產生器generator 等。
  • 聚合操作 類似SQL語句一樣的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作還有兩個基礎的特徵:

  • Pipelining: 中間操作都會返回流對象本身。 這樣多個操作可以串聯成一個管道, 如同流式風格(fluent style)。 這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路( short-circuiting)。
  • 內部迭代: 以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代。 Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。

生成流

在 Java 8 中, 集合接口有兩個方法來生成流:

  • stream() − 爲集合創建串行流。

  • parallelStream() − 爲集合創建並行流。

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

 

下面是使用Stream的常用方法的綜合實例。

部分實例來源
www.cnblogs.com/qinhao517/p/9197885.html
www.blog.csdn.net/pan_junbiao/article/details/105913518

綜合實例

1.創建實體類和數據

User.java

import java.math.BigDecimal;
 
/**
 * 用戶信息實體類
 **/
public class User
{
    private int id; //用戶ID
    private String name; //用戶名稱
    private String sex; //性別
    private int age; //年齡
    private String department; //部門
    private BigDecimal salary; //薪資
 
    //構造方法
    public User(int id,String name,String sex,int age,String department,BigDecimal salary)
    {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.department = department;
        this.salary = salary;
    }
    public int getId()
    {
        return id;
    }
    public void setId(int id)
    {
        this.id = id;
    }
    public String getName()
    {
        return name;
    } 
    public void setName(String name)
    {
        this.name = name;
    } 
    public String getSex()
    {
        return sex;
    } 
    public void setSex(String sex)
    {
        this.sex = sex;
    } 
    public int getAge()
    {
        return age;
    } 
    public void setAge(int age)
    {
        this.age = age;
    } 
    public String getDepartment()
    {
        return department;
    } 
    public void setDepartment(String department)
    {
        this.department = department;
    } 
    public BigDecimal getSalary()
    {
        return salary;
    } 
    public void setSalary(BigDecimal salary)
    {
        this.salary = salary;
    } 
    @Override
    public String toString()
    {
        return "ID:" + this.id + " 名稱:" + this.name +  " 性別:" + this.sex
                + " 年齡:" + this.age + " 部門:" + this.department + " 薪資:" + this.salary + "元";
    }
}

UserService.class

import com.pjb.streamdemo.entity.User;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
 
/**
 * 用戶信息業務邏輯類
 **/
public class UserService
{
    /**
     * 獲取用戶列表
     */
    public static List<User> getUserList()
    {
        List<User> userList = new ArrayList<User>();
        userList.add(new User(1, "青冘的博客_01", "男", 32, "研發部", BigDecimal.valueOf(1600)));
        userList.add(new User(2, "青冘的博客_02", "男", 30, "財務部", BigDecimal.valueOf(1800)));
        userList.add(new User(3, "青冘的博客_03", "女", 20, "人事部", BigDecimal.valueOf(1700)));
        userList.add(new User(4, "青冘的博客_04", "男", 38, "研發部", BigDecimal.valueOf(1500)));
        userList.add(new User(5, "青冘的博客_05", "女", 25, "財務部", BigDecimal.valueOf(1200)));
        return userList;
    }
}

2.查詢方法

2.1 forEach()

使用 forEach() 遍歷列表數據。

/**
 * 使用forEach()遍歷列表信息
 */
@Test
public void forEachTest()
{
    //獲取用戶列表
    List<User> userList = UserService.getUserList();
 
    //遍歷用戶列表
    userList.forEach(System.out::println);
}

上述遍歷語句等同於以下語句:

userList.forEach(user -> {System.out.println(user);});

執行結果:

2.2 filter(T -> boolean)

使用 filter() 過濾列表數據

獲取部門爲“研發部”的用戶列表。

/**
 * 使用filter()過濾列表信息
 */
@Test
public void filterTest()
{
    //獲取用戶列表
    List<User> userList = UserService.getUserList();
 
    //獲取部門爲“研發部”的用戶列表
    userList = userList.stream().filter(user -> user.getDepartment() == "研發部").collect(Collectors.toList());
 
    //遍歷用戶列表
    userList.forEach(System.out::println);
}

執行結果:

2.3findAny() 和 findFirst()

使用 findAny() 和 findFirst() 獲取第一條數據。

獲取用戶名稱爲“青冘的博客_02”的用戶信息,如果未找到則返回null。

/**
 * 使用findAny()獲取第一條數據
 */
@Test
public void findAnytTest()
{
        //獲取用戶列表
	    List<User> userList = UserService.getUserList();
	 
	    //獲取用戶名稱爲“青冘的博客_02”的用戶信息,如果沒有找到則返回null
	    User user = userList.stream().filter(u -> u.getName().equals("青冘的博客_02")).findAny().orElse(null);
	 
	    //打印用戶信息
	    System.out.println(user);
}

執行結果: 

注意:findFirst() 和 findAny() 都是獲取列表中的第一條數據,但是findAny()操作,返回的元素是不確定的,對於同一個列表多次調用findAny()有可能會返回不同的值。使用findAny()是爲了更高效的性能。如果是數據較少,串行地情況下,一般會返回第一個結果,如果是並行(parallelStream並行流)的情況,那就不能確保是第一個。
 

2.4map(T -> R) 和 flatMap(T -> Stream)

使用 map() 將流中的每一個元素 T 映射爲 R(類似類型轉換)。

使用 flatMap() 將流中的每一個元素 T 映射爲一個流,再把每一個流連接成爲一個流。

 

使用 map() 方法獲取用戶列表中的名稱列。

/**
 * 使用map()獲取列元素
 */
@Test
public void mapTest()
{
    //獲取用戶列表
    List<User> userList = UserService.getUserList();
 
    //獲取用戶名稱列表
    List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList());
    //或者:List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());
 
    //遍歷名稱列表
    nameList.forEach(System.out::println);
}

返回的結果爲數組類型,寫法如下:

//數組類型
String[] nameArray = userList.stream().map(User::getName).collect(Collectors.toList()).toArray(new String[userList.size()]);

執行結果:

使用 flatMap() 將流中的每一個元素連接成爲一個流。

/**
 * 使用flatMap()將流中的每一個元素連接成爲一個流
 */
@Test
public void flatMapTest()
{
    //創建城市
    List<String> cityList = new ArrayList<String>();
    cityList.add("北京;上海;深圳;");
    cityList.add("廣州;武漢;杭州;");
 
    //分隔城市列表,使用 flatMap() 將流中的每一個元素連接成爲一個流。
    cityList = cityList.stream()
            .map(city -> city.split(";"))
            .flatMap(Arrays::stream)
            .collect(Collectors.toList());
 
    //遍歷城市列表
    cityList.forEach(System.out::println);
}

執行結果:

2.5 distinct()

使用 distinct() 方法可以去除重複的數據。

獲取部門列表,並去除重複數據。

/**
 * 使用distinct()去除重複數據
 */
@Test
public void distinctTest()
{
    //獲取用戶列表
    List<User> userList = UserService.getUserList();
 
    //獲取部門列表,並去除重複數據
    List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList());
 
    //遍歷部門列表
    departmentList.forEach(System.out::println);
}

執行結果:

2.6 limit(long n) 和 skip(long n)

limit(long n) 方法用於返回前n條數據,skip(long n) 方法用於跳過前n條數據。

/**
 * limit(long n)方法用於返回前n條數據
 * skip(long n)方法用於跳過前n條數據
 */
@Test
public void limitAndSkipTest()
{
    //獲取用戶列表
    List<User> userList = UserService.getUserList();
 
    //獲取用戶列表,要求跳過第1條數據後的前3條數據
    userList = userList.stream()
            .skip(1)
            .limit(3)
            .collect(Collectors.toList());
 
    //遍歷用戶列表
    userList.forEach(System.out::println);
}

執行結果:

3.統計方法

3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)

使用 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T) 用於組合流中的元素,如求和,求積,求最大值等。

  @Test
    public void testReduce() {
       	    Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8});
	         //求集合元素之和
		    System.out.println("求集合元素之和");
	        Integer result = stream.reduce(0, Integer::sum);
	        	        System.out.println(result);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求和
	        System.out.println("求和");
	        stream.reduce((i, j) -> i + j).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求最大值
	        System.out.println("求最大值");
	        stream.reduce(Integer::max).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求最小值
	        System.out.println("求最小值");
	        stream.reduce(Integer::min).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //做邏輯
	        System.out.println("做邏輯");
	        stream.reduce((i, j) -> i > j ? j : i).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求邏輯求乘機
	        System.out.println("求邏輯求乘機");
	        int result2 = stream.filter(i -> i % 2 == 0).reduce(1, (i, j) -> i * j);

	        Optional.of(result2).ifPresent(System.out::println);
    }

執行結果:

3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long) 

int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);計算元素總和的方法其中暗含了裝箱成本,map(User::getAge) 方法過後流變成了 Stream 類型,而每個 Integer 都要拆箱成一個原始類型再進行 sum 方法求和,這樣大大影響了效率。針對這個問題 Java 8 有良心地引入了數值流 IntStream, DoubleStream, LongStream,這種流中的元素都是原始數據類型,分別是 int,double,long。 

流轉換爲數值流:

  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream
 //用戶列表中年齡的最大值、最小值、總和、平均值
    int maxVal = userList.stream().mapToInt(User::getAge).max().getAsInt();
    int minVal = userList.stream().mapToInt(User::getAge).min().getAsInt();
    int sumVal = userList.stream().mapToInt(User::getAge).sum();
    double aveVal =  userList.stream().mapToInt(User::getAge).average().getAsDouble(); 

3.3 counting() 和 count()

使用 counting() 和 count() 可以對列表數據進行統計。

 //統計研發部的人數,使用 counting()方法進行統計
    Long departCount = userList.stream().filter(user -> user.getDepartment() == "研發部").collect(Collectors.counting());
 
    //統計30歲以上的人數,使用 count()方法進行統計(推薦)
    Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();

3.4 summingInt()、summingLong()、summingDouble()

用於計算總和,需要一個函數參數。

//計算年齡總和
int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));

3.5 averagingInt()、averagingLong()、averagingDouble()

用於計算平均值

//計算平均年齡
double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章