问题
The data is something like this
head child assigned total
[ Mas Mas1 2 5
, Mas Mas2 0 5
, Usr usr1 4 4
, Usr usr2 1 3
, Inv Inv1 3 5
, Inv Inv2 2 3
, Inv Inv3 2 3
, Inv Inv4 1 3
]
I want sum of their child for a particular header.
I am using partition in sql
select head,
SUM(childAssigned) over (partition by am.acl_group) as assignedHead,
sum(childTotal) over (partition by am.acl_group) as totalHead,
child,
childAssigned,
childTotal
Since, "partition by" is not supported by HQL astquerytranslatorfactory, I am using this as a native query
I have a class which is holding SQL Response
class AclList{
String head;
Integer assignedHead;
Integer totalHead;
String child;
Integer assignedChild;
Integer totalChild;
}
The data is something like this
[ Mas 2 10 Mas1 2 5
, Mas 2 10 Mas2 0 5
, Usr 5 7 usr1 4 4
, Usr 5 7 usr2 1 3
, Inv 8 14 Inv1 3 5
, Inv 8 14 Inv2 2 3
, Inv 8 14 Inv3 2 3
, Inv 8 14 Inv4 1 3
]
I want a response which will groupify child of same header.
Response should be like ResponseClass(type, assigned, total, List)
[
{
"type":"MAS",
"assigned":"2",
"total":"10",
"subType":[
{
"type":"MAS1",
"assigned":"2",
"total":"5"
},
{
"type":"MAS2",
"assigned":"0",
"total":"5"
}
]
},
{
"type":"USR",
"assigned":"5",
"total":"7",
"subType":[
{
"type":"USR1",
"assigned":"4",
"total":"4"
},
{
"type":"USR2",
"assigned":"1",
"total":"3"
}
]
}
]
class Details(type, assigned, total) My approach is keeping header in hashmap's key, child in hashmap's value Since all 3 columns of a head are same, So, equals, hashcode take care.
class ChildDetails(List) HashMap
Iterate sql response
if hashmap.contains(head) //exist
fetch value, add new one in list
hashmap.put(head, updated)
else
create header,
create child, add it to a blank list
hashmap.put(head, new list)
Again iterate hashmap, arrange it in new json response
But, this is a cumbersome proces & inefficient.
Is there anyway it can be done with JAVA stream() ?
回答1:
Yes it can be done with JAVA stream()
package com.bbc.enums;
import lombok.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.*;
import java.util.stream.Collectors;
public class StackOverFlow {
public static void main(String[] args) {
List<AclList> aclLists = Arrays.asList(
new AclList("Mas", 2, 10, "Mas1", 2, 5),
new AclList("Mas", 2, 10, "Mas2", 0, 5),
new AclList("USR", 5, 7, "USR1", 4, 4),
new AclList("USR", 5, 7, "USR2", 1, 3),
new AclList("Inv", 8, 14, "Inv1", 3, 5),
new AclList("Inv", 8, 14, "Inv2", 2, 3),
new AclList("Inv", 8, 14, "Inv3", 2, 3),
new AclList("Inv", 8, 14, "Inv4", 1, 3)
);
Map<String, Map<Integer, Map<Integer, List<AclList>>>> collect = aclLists.stream()
.collect(Collectors.groupingBy(AclList::getHead, Collectors.groupingBy(AclList::getAssignedHead,
Collectors.groupingBy(AclList::getTotalHead))));
List<HashMap<String, Object>> resList = new ArrayList<>();
collect.forEach((k1, v1) -> {
HashMap<String, Object> res = new HashMap<>();
res.put("type", k1);
v1.forEach((k2, v2) -> {
res.put("assigned", k2);
v2.forEach((k3, v3) -> {
res.put("total", k3);
List<HashMap<String, Object>> list = new ArrayList<>();
v3.forEach(aclList -> {
HashMap<String, Object> map1 = new HashMap<>();
map1.put("type", aclList.getChild());
map1.put("assigned", aclList.getAssignedChild());
map1.put("total", aclList.getTotalChild());
list.add(map1);
});
res.put("subType", list);
});
});
resList.add(res);
});
System.out.println(resList);
// if you want json response then
System.out.println(listmap_to_json_string(resList));
}
public static String listmap_to_json_string(List<HashMap<String, Object>> list) {
JSONArray json_arr = new JSONArray();
for (Map<String, Object> map : list) {
JSONObject json_obj = new JSONObject();
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
try {
json_obj.put(key, value);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
json_arr.put(json_obj);
}
return json_arr.toString();
}
}
@AllArgsConstructor
@Getter
@Setter
@ToString
@NoArgsConstructor
class AclList {
String head;
Integer assignedHead;
Integer totalHead;
String child;
Integer assignedChild;
Integer totalChild;
}
回答2:
The Collectors.groupingBy stream Collector
can help you here.
Setup:
// original data AclList
class AclList {
String head;
Integer assignedHead;
Integer totalHead;
String child;
Integer assignedChild;
Integer totalChild;
}
// Subtype in the response object based on Json
class SubType {
String type;
Integer assigned;
Integer total;
public SubType(String type, Integer assigned, Integer total) {
this.type = type;
this.assigned = assigned;
this.total = total;
}
}
// Response object based on Json
class Response {
String type;
Integer assigned;
Integer total;
List<SubType> subType;
public Response(String type, Integer assigned, Integer total, List<SubType> subType) {
this.type = type;
this.assigned = assigned;
this.total = total;
this.subType = subType;
}
}
Step 1: Group the data by the head
field in the class AclList
. This step returns a Map
of the distinct head
values as the key, and a list of AclList
objects as the value, grouped by the key.
Map<String, List<AclList>> groupedByHead = data
.stream()
.collect(Collectors.groupingBy(acl -> acl.head));
Step 2: Just manipulate the Map
to create the required response structure.
List<Response> response = groupedByHead.entrySet()
.stream()
.filter(e -> e.getValue().size() > 0)
.map(e -> {
List<AclList> acls = e.getValue();
AclList first = acls.get(0);
List<SubType> children = acls
.stream()
.map(acl -> new SubType(acl.child, acl.assignedChild, acl.totalChild))
.collect(Collectors.toList());
return new Response(first.head, first.assignedHead, first.totalHead, children);
})
.collect(Collectors.toList());
Note: I'm not sure about your constraints, but it would be better do just do the grouping operations in your database queries instead of doing it in code.
来源:https://stackoverflow.com/questions/58674170/grouping-a-class-using-stream-java-8-or-in-sql-whichever-is-best