I want to convert below for loop to Java 8. But having problem with filtering list of status and grouping into one status and total count. I tried but for each "LIVE"
, "DRAFT"
, "TEST"
have to loop 3 times and get 3 different maps. Is it possible to get into one loop using Java-8?
Where "LIVE"
, "DRAFT"
and "TEST"
are again combination of status from workflowInstance
like DRAFT = {"DRAFT_EDIT","DRAFT_SAVE"}
. I want to categorize all status into 3 based on this combination defined.
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 individually for "LIVE"
, "DRAFT"
and "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"
Instead of looping 3 times I want to do in 1 go and categorize them. Any help would be appreciated.
You cannot avoid extracting the type of the status anyway. Create a dedicated method for it (I suppose the list of statuses as liveStatuses
etc. are either static or instance variables. Note that you have forgotten to handle the case no one of the predefined statuses match the current one. In that case, let's use "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
}
Then the collecting is fairly easy using Collectors.groupingBy
with a combination of Collectors.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
Note the result is Map<String, Long>
if you insist on Map<String, Integer>
you need an additional downstream collector using 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
Using The ArithmeticException
is thrown if the number is outside bounds. Remember that Long
has the way bigger range than Integer
. There are many different ways of the conversion of Long
->Integer
but they follow the same principle.
... or use a simple trick using Collectors.summingInt(e -> 1)
instead of Collectors.counting
as @HadiJ suggested. It returns Integer
instead:
Map<String, Integer> map = workflowInstances.stream()
.collect(Collectors.groupingBy(this::extractStatus, Collectors.summingInt(e -> 1)));
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments