有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用Java流的lambda并行循环?

我有两张单子。其中一个显示了一组人中每个人在某个游戏中成功尝试的次数

public class SuccessfulAttempts{
    String name;
    int successCount;
}

List<SuccessfulAttempts> success;

以及每个人的总尝试次数

public class TotalAttempts{
    String name;
    int totalCount;
}

List<TotalAttempts> total;

我想展示小组中每个人的成功率

public class PercentageSuccess{
    String name;
    float percentage;
}

List<PercentageSuccess> percentage;

假设我已经像这样填充了前两个列表

success.add(new SuccessfulAttempts(Alice, 4));
success.add(new SuccessfulAttempts(Bob, 7));

total.add(new TotalAttempts(Alice, 5));
total.add(new TotalAttempts(Bob, 10));

现在我想计算每个使用Java流的人的成功百分比。所以我实际上需要列表List<PercentageSuccess> percentage的这种结果

new PercentageSuccess(Alice, 80);
new PercentageSuccess(Bob, 70);

我想在并行中计算它们(Alice的百分比和Bob的百分比)(我知道如何使用循环按顺序进行)。如何使用Java流(或任何其他简单的方法)实现这一点


共 (3) 个答案

  1. # 1 楼答案

    如果数组具有相同的size并且顺序正确,则可以使用整数索引访问原始列表元素

    List<PercentageSuccess> result = IntStream.range(0, size).parallel().mapToObj(index -> /*get the elements and construct percentage progress for person with given index*/).collect(Collectors.toList())
    

    这意味着您必须为PercentageSuccess创建一个方法或custructor,它为给定的SuccessAttempts和TotalAttempts构造一个百分比

    PercentageSuccess(SuccessfulAttempts success, TotalAttempts total) {
        this.name = success.name;
        this.percentage = (float) success.successCount / (float) total.totalCount;
    }
    

    然后构造一个从0到大小的并行整数流:

    IntStream.range(0, size).parallel()
    

    这实际上是并行for循环。然后将每个整数转换为索引第四个人的成功百分比(请注意,您必须确保列表大小相同,并且没有混乱,否则我的代码不正确)

    .mapToObj(index -> new PercentageSuccess(success.get(index), total.get(index))
    

    最后,使用

    .collect(Collectors.toList())
    

    此外,如果successtotal是LinkedList或其他列表实现,则这种方法不是最优的,按索引访问元素的成本为O(n)

  2. # 2 楼答案

    private static List<PercentageAttempts> percentage(List<SuccessfulAttempts> success, List<TotalAttempts> total) {
    
        Map<String, Integer> successMap = success.parallelStream()
                .collect(Collectors.toMap(SuccessfulAttempts::getName, SuccessfulAttempts::getSuccessCount, (a, b) -> a + b));
    
        Map<String, Integer> totalMap = total.parallelStream()
                .collect(Collectors.toMap(TotalAttempts::getName, TotalAttempts::getTotalCount));
    
        return successMap.entrySet().parallelStream().map(entry -> new PercentageAttempts(entry.getKey(),
                entry.getValue() * 1.0f / totalMap.get(entry.getKey()) * 100))
                .collect(Collectors.toList());
    
    }
    
  3. # 3 楼答案

    我建议将您的列表之一转换为地图,以便更方便地访问计数。否则,对于一个列表的每个值,您必须在另一个列表中循环,这将是O(n^2)复杂度

    List<SuccessfulAttempts> success = new ArrayList<>();
    List<TotalAttempts> total = new ArrayList<>();
    
    success.add(new SuccessfulAttempts("Alice", 4));
    success.add(new SuccessfulAttempts("Bob", 7));
    
    total.add(new TotalAttempts("Alice", 5));
    total.add(new TotalAttempts("Bob", 10));
    
    // First create a Map
    Map<String, Integer> attemptsMap = success.parallelStream()
        .collect(Collectors.toMap(SuccessfulAttempts::getName, SuccessfulAttempts::getSuccessCount));
    
    // Loop through the list of players and calculate percentage.
    List<PercentageSuccess> percentage =
        total.parallelStream()
             // Remove players who have not participated from List 'total'. ('attempt' refers to single element in List 'total').
             .filter(attempt -> attemptsMap.containsKey(attempt.getName()))
             // Calculate percentage and create the required object
             .map(attempt -> new PercentageSuccess(attempt.getName(),
                        ((attemptsMap.get(attempt.getName()) * 100) / attempt.getTotalCount())))
             // Collect it back to list
             .collect(Collectors.toList());
    
    percentage.forEach(System.out::println);