Java Stream六個使用舉例

banq發表於2024-05-01

Java Streams 提供了一種處理物件集合的函式式方法。它們允許簡潔且富有表現力的程式碼,從而更容易對資料集執行復雜的操作。透過利用 Java Streams,我們可以簡化統計組織中男性和女性員工的流程,從而提供比傳統迭代方法更有效的解決方案。

1、使用 Java Streams 按性別查詢員工人數
跟蹤員工性別分佈等人口統計資料不僅對於合規性至關重要,而且對於培育包容性環境也至關重要。隨著 Java Streams 的出現,處理資料操作變得更加高效和優雅,我們將探討如何利用 Java Streams 來查詢組織中的男性和女性員工數量。

設定資料
在深入研究程式碼之前,讓我們建立一個假設場景,其中我們有一個代表組織中個人的員工類。每個員工物件包含姓名、年齡、部門和性別等屬性。我們將使用此資料結構來演示如何利用 Java Streams 來統計男性和女性員工的數量。

class Employee {
    String name;
    int age;
    String department;
    String gender;

    // Constructor, getters, setters
}

統計男性和女性員工
定義了員工類別後,讓我們繼續計算使用 Java Streams 的男性和女性員工的數量。

import java.util.List;
import java.util.stream.Collectors;

public class EmployeeStatistics {

    public static void main(String[] args) {
        List<Employee> employees = // Initialize list of employees
        
        long maleCount = employees.stream()
                                .filter(e -> e.getGender().equalsIgnoreCase("male"))
                                .count();

        long femaleCount = employees.stream()
                                  .filter(e -> e.getGender().equalsIgnoreCase("female"))
                                  .count();

        System.out.println("Male employees: " + maleCount);
        System.out.println("Female employees: " + femaleCount);
    }
}


在上面的程式碼中,我們首先從員工列表中建立一個流。然後,我們使用 filter() 方法僅保留性別符合指定條件(“男性”或“女性”)的員工。最後,我們使用 count() 方法分別確定男性和女性員工的數量。

Java Streams 提供了一種強大的機制,可以以實用且簡潔的方式處理資料。透過利用 Java Streams,我們可以有效地統計組織中的男性和女性員工數量,從而有助於更好的資料管理並培育更具包容性的工作場所文化。隨著組織繼續優先考慮多樣性和包容性,利用 Java Streams 等工具進行資料分析變得越來越有價值。無論是性別多樣性還是任何其他人口統計指標,Java Streams 都為處理資料操作提供了靈活高效的解決方案。

2、使用 Java 8 Streams 查詢每個部門的最高薪員工
想象一下,我們有一組員工,每個員工都屬於一個特定的部門並擁有相應的薪水。我們的目標是找到每個部門的最高薪水。傳統上,此任務可能涉及巢狀迴圈或複雜的邏輯。然而,使用Java Streams,我們可以以簡潔而優雅的方式實現這一點。

import java.util.*;
import java.util.stream.Collectors;

class Employee {
    private String name;
    private String department;
    private double salary;

    public Employee(String name, String department, double salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public double getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", department='" + department + '\'' +
                ", salary=" + salary +
                '}';
    }
}

public class Main {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
                new Employee("John", "HR", 50000),
                new Employee("Alice", "IT", 60000),
                new Employee("Bob", "HR", 55000),
                new Employee("Jane", "IT", 62000),
                new Employee("Eva", "Finance", 70000),
                new Employee("Michael", "Finance", 75000)
        );

        Map<String, Optional<Employee>> highestSalariesByDept = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                        Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary))));

        // Print the result
        highestSalariesByDept.forEach((dept, employee) -> {
            System.out.println("Department: " + dept +
                    ", Highest Salary: " + (employee.isPresent() ? employee.get().getSalary() : "N/A"));
        });
    }
}

在此程式碼片段中,我們定義了一個 Employee 類,該類代表具有姓名、部門和工資等屬性的單個員工。然後,我們建立一個 Employee 物件列表來模擬我們的資料集。

神奇的事情發生在主方法中,我們使用 Java Streams 處理僱員列表。我們首先使用 Collectors.groupingBy 將員工按部門分組。在每個部門組中,我們使用 Collectors.maxBy 找到工資最高的員工。最後,我們列印出結果,顯示部門及其最高工資。

透過利用 Java Streams 及其函數語言程式設計功能,我們簡潔地解決了查詢每個部門最高工資的問題。這種方法不僅簡化了程式碼,而且使程式碼更具可讀性和可維護性。


3、查詢列表中最長的連續整數序列
使用 Java 時,Stream API 提供了一種強大而簡潔的方法來對集合執行操作。在這篇博文中,我們將探討如何使用 Java Stream API 查詢列表中最長的連續整數序列。

問題陳述
給定一個整數列表,我們想要找到最長的連續整數序列。例如,給定輸入 [100, 4, 200, 1, 3, 2],最長連續序列為 [1, 2, 3, 4]。

方法
為了使用 Stream API 解決這個問題,我們可以按照以下步驟操作:

1. 將整數列表轉換為集合以刪除重複項並啟用快速查詢。
2. 迭代集合中的每個元素。
3. 對於每個元素,檢查集合中是否存在前一個整數(元素 - 1)。如果不存在,則意味著當前元素是新的連續序列的開始。
4. 繼續檢查連續整數,直到不再有連續整數為止。
5. 跟蹤每個連續序列的長度並返回找到的最長序列的長度。

執行
下面是實現上述方法的 Java 程式碼:

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class LongestConsecutiveSequence {

    public static int findLongestConsecutiveSequence(List<Integer> nums) {
        Set<Integer> numSet = new HashSet<>(nums);
        int longestSeq = 0;

        for (int num : numSet) {
            if (!numSet.contains(num - 1)) {
                int currentNum = num;
                int currentSeq = 1;

                while (numSet.contains(currentNum + 1)) {
                    currentNum++;
                    currentSeq++;
                }

                longestSeq = Math.max(longestSeq, currentSeq);
            }
        }

        return longestSeq;
    }

    public static void main(String[] args) {
        List<Integer> nums = List.of(100, 4, 200, 1, 3, 2);
        int longestSeq = findLongestConsecutiveSequence(nums);
        System.out.println("Longest consecutive sequence length: " + longestSeq);
    }
}

在此實現中,我們首先將整數列表 nums 轉換為集合 numSet。然後我們迭代 numSet 中的每個元素。對於每個元素,我們檢查 numSet 中是否存在前一個整數。如果沒有,我們開始從該元素開始計算連續序列。我們繼續計數,直到不再有連續的整數,並用找到的最大序列長度更新longestSeq。

使用 Java Stream API,我們可以有效地找到列表中最長的連續整數序列。這種方法展示了 Stream API 在處理 Java 集合上的複雜操作時的強大功能和簡單性。

4、尋找圖中的最大團clique 
尋找圖中的最大團是電腦科學和圖論中的一個經典問題。團是無向圖中頂點的子集,子集中的每對頂點都透過邊連線。最大團是圖中最大的團。在這篇博文中,我們將探討如何使用 Java Stream API 查詢圖中的最大派系。

圖表示
我們將圖表示為鄰接矩陣,其中如果頂點“i”和“j”之間存在邊,則“graph[j]”為 true,否則為 false。該圖將表示為二維布林陣列。

boolean[][] graph = {
    {false, true, true, false, false},
    {true, false, true, true, false},
    {true, true, false, true, true},
    {false, true, true, false, true},
    {false, false, true, true, false}
};

在此示例中,圖有 5 個頂點,鄰接矩陣表示以下邊:

  • - 頂點 0 連線到頂點 1 和 2。
  • - 頂點 1 連線到頂點 0、2 和 3。
  • - 頂點 2 連線到頂點 0、1、3 和 4。
  • - 頂點 3 連線到頂點 1、2 和 4。
  • - 頂點 4 連線到頂點 2 和 3。

尋找團派
為了找到圖中的所有團派,我們將使用遞迴演算法生成所有可能的頂點子集並檢查每個子集是否是團派。然後,我們將從所有團派系列表中過濾掉最大團派系。

import java.util.*;
import java.util.stream.*;

public class MaximumCliqueFinder {

    public static List<List<Integer>> findCliques(boolean[][] graph) {
        List<List<Integer>> cliques = new ArrayList<>();
        List<Integer> currentClique = new ArrayList<>();
        Set<Integer> candidates = IntStream.range(0, graph.length).boxed().collect(Collectors.toSet());
        findCliquesUtil(graph, cliques, currentClique, candidates);
        return cliques;
    }

    private static void findCliquesUtil(boolean[][] graph, List<List<Integer>> cliques,
                                        List<Integer> currentClique, Set<Integer> candidates) {
        if (candidates.isEmpty()) {
            cliques.add(new ArrayList<>(currentClique));
            return;
        }

        List<Integer> candidatesList = new ArrayList<>(candidates);
        for (Integer candidate : candidatesList) {
            Set<Integer> newCandidates = new HashSet<>(candidates);
            newCandidates.retainAll(getNeighbors(candidate, graph));

            currentClique.add(candidate);
            findCliquesUtil(graph, cliques, currentClique, newCandidates);
            currentClique.remove(candidate);

            candidates.remove(candidate);
        }
    }

    private static Set<Integer> getNeighbors(int vertex, boolean[][] graph) {
        Set<Integer> neighbors = new HashSet<>();
        for (int i = 0; i < graph[vertex].length; i++) {
            if (graph[vertex][i]) {
                neighbors.add(i);
            }
        }
        return neighbors;
    }

    public static List<List<Integer>> findMaximumCliques(boolean[][] graph) {
        List<List<Integer>> allCliques = findCliques(graph);
        int maxSize = allCliques.stream().mapToInt(List::size).max().orElse(0);
        return allCliques.stream().filter(clique -> clique.size() == maxSize).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        boolean[][] graph = {
            {false, true, true, false, false},
            {true, false, true, true, false},
            {true, true, false, true, true},
            {false, true, true, false, true},
            {false, false, true, true, false}
        };

        List<List<Integer>> maximumCliques = findMaximumCliques(graph);
        System.out.println("Maximum Cliques:");
        for (List<Integer> clique : maximumCliques) {
            System.out.println(clique);
        }
    }
}


在此程式碼中,

  • “findCliques”方法使用遞迴方法生成圖中的所有團派系。
  • ` findMaximumCliques ` 方法根據團派的大小從所有團派的列表中過濾掉最大團派。

我們將圖表示為鄰接矩陣,並實現了一種演算法來查詢圖中的所有團派系。最後,我們從所有派系列表中過濾掉最大團派系。這種方法展示了 Java Stream API 在解決圖相關問題方面的強大功能和靈活性。


5、使用 Java 流應用程式介面查詢圖形中的最大切割Cut 
圖論是電腦科學的一個基本領域,涉及圖的研究,圖是用於建模物件之間成對關係的數學結構。在圖論中,切割Cut是將圖的頂點劃分為兩個不相交的子集,最大切割Cut是兩個子集之間邊數最多的割。

瞭解圖中的最大切割
給定一個無向圖 `G = (V, E)`,其中 `V` 是頂點集,`E` 是邊集,切割 `C = (S, V - S)` 是將頂點劃分為兩個不相交的子集 `S` 和 `V - S`。切分的大小是指一個端點在 `S` 中,另一個端點在 `V - S` 中的邊的數量。

最大剪下問題的目的是在圖中找到一個切割,使穿過切割的邊的數量達到最大。

方法
要找到圖中的最大切割,我們將使用以下方法:

  1. 初始化兩個集合`S`和`T`,分別代表兩個頂點子集。
  2. 遍歷圖中的所有邊。
  3. 對於每條邊,如果其端點位於不同的集合(`S`和`T`)中,則將該邊新增到剪下中。
  4. 重複上述過程,直到沒有邊可以新增到剪下中。

Java 實現
我們將使用鄰接表來表示圖形,其中每個頂點都與其相鄰頂點的列表相關聯。

import java.util.*;

public class MaximumCut {

    public static void main(String[] args) {
        int vertices = 4;
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < vertices; i++) {
            graph.add(new ArrayList<>());
        }

        // Adding edges to the graph
        addEdge(graph, 0, 1);
        addEdge(graph, 0, 2);
        addEdge(graph, 1, 2);
        addEdge(graph, 1, 3);
        addEdge(graph, 2, 3);

        // Find maximum cut
        Set<Integer> S = new HashSet<>();
        Set<Integer> T = new HashSet<>();
        Set<List<Integer>> maxCut = new HashSet<>();
        int maxCrossEdges = 0;

        for (int v = 0; v < vertices; v++) {
            if (S.contains(v)) {
                T.add(v);
            } else {
                S.add(v);
            }
        }

        for (int v = 0; v < vertices; v++) {
            for (int neighbor : graph.get(v)) {
                if ((S.contains(v) && T.contains(neighbor)) || (S.contains(neighbor) && T.contains(v))) {
                    List<Integer> edge = Arrays.asList(v, neighbor);
                    maxCut.add(edge);
                    maxCrossEdges++;
                }
            }
        }

        System.out.println("Maximum Cut:");
        for (List<Integer> edge : maxCut) {
            System.out.println(edge.get(0) + " - " + edge.get(1));
        }
        System.out.println("Number of Cross Edges: " + maxCrossEdges);
    }

    public static void addEdge(List<List<Integer>> graph, int u, int v) {
        graph.get(u).add(v);
        graph.get(v).add(u);
    }
}

在此實現中,我們首先建立一個有四個頂點的圖的鄰接表。然後,我們遍歷圖中的所有邊,檢查每條邊的端點是否屬於不同的集合 `S` 和 `T`。如果是,我們就將該邊新增到最大切割中,並更新交叉邊的計數。

最後,我們會列印最大剪下以及交叉邊的數量。

我們討論了圖論中的最大切割問題,並使用 Java Stream API 實現了一種查詢圖中最大切割的演算法。該演算法遍歷圖中的所有邊,如果邊的端點屬於不同的子集,則將其新增到最大切割中。這種方法展示瞭如何使用 Java 流處理圖資料結構。

6、Java Stream API:Tarjan 的強連線元件演算法
強連線元件 (SCC) 在圖論中發揮著至關重要的作用,有助於識別有向圖中的節點組,其中每個節點都可以從其他節點到達。 Tarjan 演算法是一種在圖中高效查詢 SCC 的經典方法。我們將深入研究使用 Java 實現 Tarjan 演算法,利用 Java Stream API 的強大功能來提供簡潔而優雅的解決方案。

理解 Tarjan 演算法:
Tarjan 演算法是一種圖演算法,用於查詢有向圖中的強連通分量。該演算法由 Robert Tarjan 於 1972 年開發,基於深度優先搜尋遍歷。它透過維護有關遍歷中每個節點的深度和每個節點的最低可達祖先的資訊來有效地識別 SCC。這使得它能夠識別後邊緣,從而檢測 SCC。

使用 Java Stream API 實現 Tarjan 演算法:
Java Stream API 提供了一種強大的方法來處理集合並以函式方式對其執行操作。利用 Stream API 可以生成簡潔且可讀的程式碼。讓我們看看如何使用 Java Stream API 實現 Tarjan 演算法來查詢 SCC。

實施步驟:

  1. 定義一個類來表示圖節點,包括跟蹤節點索引、lowlink 值以及節點是否在堆疊上的屬性。
  2. 使用更新低鏈路值並識別 SCC 的深度優先搜尋遍歷函式來實現 Tarjan 演算法。
  3. 利用Stream API對集合進行過濾、對映等操作,簡化實現。
  4. 使用樣本有向圖測試實現,以驗證其正確性和效率。

import java.util.*;

class GraphNode {
    int index;
    int lowlink;
    boolean onStack;

    GraphNode(int index) {
        this.index = index;
        this.lowlink = index;
        this.onStack = false;
    }
}

public class TarjansAlgorithm {

    private int index;
    private Deque<GraphNode> stack;
    private List<List<Integer>> graph;
    private List<List<Integer>> stronglyConnectedComponents;

    public List<List<Integer>> findSCCs(List<List<Integer>> graph) {
        this.index = 0;
        this.stack = new ArrayDeque<>();
        this.graph = graph;
        this.stronglyConnectedComponents = new ArrayList<>();

        for (int i = 0; i < graph.size(); i++) {
            if (graph.get(i) != null && !graph.get(i).isEmpty()) {
                if (graph.get(i).get(0) == -1) {
                    continue; // Skip visited nodes
                }
                dfs(i);
            }
        }

        return stronglyConnectedComponents;
    }

    private void dfs(int v) {
        GraphNode node = new GraphNode(v);
        stack.push(node);
        node.onStack = true;
        node.lowlink = index;
        index++;

        for (int neighbor : graph.get(v)) {
            if (graph.get(neighbor) != null && !graph.get(neighbor).isEmpty()) {
                if (graph.get(neighbor).get(0) == -1) {
                    continue; // Skip visited nodes
                }
                if (stack.contains(new GraphNode(neighbor))) {
                    node.lowlink = Math.min(node.lowlink, neighbor);
                } else {
                    dfs(neighbor);
                    node.lowlink = Math.min(node.lowlink, neighbor);
                }
            }
        }

        if (node.lowlink == v) {
            List<Integer> component = new ArrayList<>();
            GraphNode w;
            do {
                w = stack.pop();
                w.onStack = false;
                component.add(w.index);
            } while (w.index != v);
            stronglyConnectedComponents.add(component);
        }
    }

    public static void main(String[] args) {
        TarjansAlgorithm tarjansAlgorithm = new TarjansAlgorithm();

        List<List<Integer>> graph = new ArrayList<>();
        graph.add(Arrays.asList(1)); // Node 0 -> Node 1
        graph.add(Arrays.asList(2)); // Node 1 -> Node 2
        graph.add(Arrays.asList(0)); // Node 2 -> Node 0

        List<List<Integer>> sccs = tarjansAlgorithm.findSCCs(graph);
        System.out.println("Strongly Connected Components: " + sccs);
    }
}

在本博文中,我們探討了在有向圖中查詢強連線元件的 Tarjan 演算法,並使用 Java Stream API 實現了該演算法。透過利用 Stream API,我們能夠編寫簡潔可讀的程式碼,同時高效地識別圖中的 SCC。Tarjan 演算法仍然是圖論中的一個基本工具,在 Java 中實現該演算法對任何程式設計師來說都是一個寶貴的工具包。

 

相關文章