【问题标题】:using Java 8 How to filter by list and group by based on filter condition and convert to Map with their count使用 Java 8 如何根据过滤条件按列表过滤和分组,并使用它们的计数转换为 Map
【发布时间】:2020-08-26 23:48:45
【问题描述】:

我想将下面的 for 循环转换为 Java 8。但是在过滤状态列表和分组为一个状态和总数时遇到问题。 我试过了,但是对于每个"LIVE""DRAFT""TEST" 必须循环 3 次并获得 3 个不同的地图。使用Java-8是否可以进入一个循环?

其中"LIVE""DRAFT""TEST" 又是来自workflowInstance 的状态组合,例如DRAFT = {"DRAFT_EDIT","DRAFT_SAVE"}。我想根据定义的这个组合将所有状态分类为 3。

Map<String, Integer> summaryMap = new HashMap<>();
int l = 0, d = 0, t = 0;

for (WorkflowInstance instance : workflowInstances) {
    if (liveStatuses.contains(instance.getStatus())) {
        summaryMap.put("LIVE", l++);
    } else if (testStatuses.contains(instance.getStatus())) {
        summaryMap.put("TEST", t++);
    } else if (draftStatuses.contains(instance.getStatus())) {
        summaryMap.put("DRAFT", d++);
    }
}

Java-8 分别用于 "LIVE""DRAFT""TEST"

map.put("DRAFT", workflowInstances.stream()
     .filter(inst-> Constants.DRAFT_STATUS.contains(inst.getStatus()))
     .collect(Collectors.groupingBy(WorkflowInstance::getStatus, Collectors.counting()))
     .entrySet().stream().mapToLong(e-> e.getValue()).sum()
);

map.put("LIVE", workflowInstances.stream()
     .filter(inst-> Constants.LIVE_STATUS.contains(inst.getStatus()))
     .collect(Collectors.groupingBy(WorkflowInstance::getStatus, Collectors.counting()))
     .entrySet().stream().mapToLong(e-> e.getValue()).sum()
);

// Similar for "TEST"

我不想循环 3 次,而是想一次性完成并对它们进行分类。 任何帮助将不胜感激。

【问题讨论】:

  • 我不确定我是否理解正确,但我认为您不需要过滤任何内容。只需收集 groupingBy 就可以了 - 你得到了一张以状态为键并计为值的地图。
  • 我想从 workflowInstance 对象中过滤状态,例如 DRAF_EDIT、DRAFT_SAVE 等,但想将它们组合为 DRAFT。
  • 您的summaryMap 已经拥有此信息。为什么要进一步处理?

标签: java dictionary java-8 java-stream


【解决方案1】:

检查此代码: 我的解决方案添加了一个枚举来处理不同类型的多个状态。

public static void main(String[] args) {
        List<Instance> instances = new ArrayList<>();
        instances.add(new Instance("LIVE"));
        instances.add(new Instance("TEST"));
        instances.add(new Instance("TEST"));
        instances.add(new Instance("TEST"));
        instances.add(new Instance("DRAFT"));
        instances.add(new Instance("DRAFT"));

        Map<String, Long> counts = instances.stream()
.collect(Collectors.groupingBy(a -> TemplateStatus.getStatus(a.getStatus()),Collectors.counting());

        System.out.println(counts);  //output: {DRAFT=2, TEST=3, LIVE=1}

    }



class Instance {
    public Instance(String status) {
        this.status = status;
    }

    private String status;

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}

enum TemplateStatus {
    LIVE("LIVE,LIVE2"), DRAFT("DRAFT,DRAFT_2"), TEST("TEST_1,TEST");

    private List<String> status;

    TemplateStatus(String s) {
        status = Arrays.asList(s.split(","));
    }

    public static String getStatus(String s) {
        if (LIVE.status.contains(s)) return "LIVE";
        else if (TEST.status.contains(s)) return "TEST";
        else if (DRAFT.status.contains(s)) return "DRAFT";
        return "UNKNOWN";
    }
}

希望对你有帮助

【讨论】:

  • @Amongalen 我使用Enum 解决了它。希望这会有所帮助。
【解决方案2】:

我认为您需要做的就是创建一个更复杂的分组函数,将getStatus 转换为您想要的三种类型之一。你可以试试这样的:

Map<String, Long> summaryMap = workflowInstances.stream()
    .groupingBy(a -> {
        if (liveStatuses.contains(a.getStatus())) {
            return "LIVE";
        } else if (testStatuses.contains(a.getStatus())) {
            return "TEST";
        } else if (draftStatuses.contains(a.getStatus())) {
            return "DRAFT";
        }
     }, Collectors.counting());

【讨论】:

    【解决方案3】:

    无论如何,您都无法避免提取状态的类型。为它创建一个专用方法(我想liveStatuses等状态列表是静态变量或实例变量。请注意,您忘记处理没有任何一个预定义状态与当前状态匹配的情况。在这种情况下,让我们使用"UNDEFINED":

    String extractStatus(WorkflowInstance workflowInstance) {
        String status = workflowInstance.getStatus();
        if (liveStatuses.contains(status)) {
            return "LIVE";
        } else if (testStatuses.contains(status)) {
            return "TEST";
        } else if (draftStatuses.contains(status)) {
            return "DRAFT";
        }
        return "UNCATEGORIZED";                          // in case nothing is matched
    }
    

    然后使用Collectors.groupingByCollectors.counting 的组合来收集相当容易:

    Map<String, Long> map = workflowInstances.stream()
        .collect(Collectors.groupingBy(                  // groups to Map
                this::extractStatus,                     // extracted status is the key
                Collectors.counting()));                 // value is a number of occurences
    

    注意结果是Map&lt;String, Long&gt;,如果你坚持使用Map&lt;String, Integer&gt;,你需要一个额外的下游收集器使用Collectors.collectingAndThen

    Map<String, Integer> map = workflowInstances.stream()
        .collect(Collectors.groupingBy(                   // groups to Map
                Foo::extractStatus,                       // extracted status is the key
                Collectors.collectingAndThen(             // value is collected ...
                        Collectors.counting(),            // ... a number of occurences
                        count -> new BigDecimal(count)    // ... as Integer from Long
                                     .intValueExact()))); // ... but might throw an exception 
    

    使用 如果数字超出范围,则抛出 ArithmeticException。请记住,Long 的范围比Integer 大得多。 Long->Integer的转换方式有很多种,但都遵循相同的原则。

    ... 或使用Collectors.summingInt(e -&gt; 1) 代替Collectors.counting 的简单技巧,如@HadiJ 建议的那样。它返回Integer

    Map<String, Integer> map = workflowInstances.stream()
        .collect(Collectors.groupingBy(this::extractStatus, Collectors.summingInt(e -> 1)));
    

    【讨论】:

    • 如果你有 instance 变量,则在第 2 和第 3 中 - 该变量未在该上下文中声明。应改为status
    • 我认为您可以使用第二种方式Map&lt;String, Integer&gt; map = workflowInstances.stream() .collect(Collectors.groupingBy( this::extractStatus, Collectors.summingInt(e-&gt;1)));
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 2022-08-14
    • 2018-09-17
    • 2018-11-15
    • 2019-01-26
    • 1970-01-01
    • 2018-05-25
    相关资源
    最近更新 更多