Java Stream六个使用举例

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 中实现该算法对任何程序员来说都是一个宝贵的工具包。