patternjavaMinor
Joining a product-application-customer relationship using Java 8 Streams
Viewed 0 times
applicationproductjavausingstreamsjoiningcustomerrelationship
Problem
I have three classes: product, customer and application.
The schema of these classes are as follows:
The only way of connection between product and customer is through the application.
The problem in hand is to retrieve all products with their associated customers (appID and customerID) in this JSON format.
I have done this using a nested for loop:
Here,
Furthermore, I have converted this into the following which utilizes Java 8 Stream API:
```
List prodList = products.stream()
.map(product -> {
List customerList =searchResults.getResult().stream()
The schema of these classes are as follows:
class application {
String productID;
String customerID;
String applicationId;
String name;
....
// getters and setters and associated constructors
}
class product {
String name;
String ID;
...
// constructors and getters and setters
}
class customer {
String name;
String ID;
...
// constructors and getters and setters
}The only way of connection between product and customer is through the application.
The problem in hand is to retrieve all products with their associated customers (appID and customerID) in this JSON format.
[
{
"product": {
// product details
}
"customers": [ {
"application": ,
"ID": } ]
}
]I have done this using a nested for loop:
for (Product product : products) {
ProductInfo prodInfo = new
ProductInfo();
prodInfo.setProduct(product);
List associatedCustomerList = new ArrayList<>();
for (Object res : searchResults.getResult()) {
if ((product.getId()).equals((String) ((Map) res).get("productID"))) {
CustomerInfo associatedCustomer = new CustomerInfo();
associatedCustomer.setCustomerID((String) ((Map) res).get("customerID"));
associatedCustomer.setApplicationId((String) ((Map) res).get("appId"));
associatedCustomerList.add(associatedCustomer);
}
prodInfo.setCustomers(associatedCustomerList);
}
productInfo.add(prodInfo);
}Here,
searchResults contain the result from List. CustomerDetails contain appId and customerId as attributes. ProductInformationWithAssociatedCustomer is the response class.Furthermore, I have converted this into the following which utilizes Java 8 Stream API:
```
List prodList = products.stream()
.map(product -> {
List customerList =searchResults.getResult().stream()
Solution
Using the appropriate types
Your are repeatedly casting
Rationale: Avoid code repetition and makes the one-time cast clearer to understand
Grouping the search results into a usable
Instead of traversing your
Rationale: Consider when you have \$n\$ products and a result for each of them. Your current solution will effectively be iterating through them, and iterate through the \$n\$ results for the one matching product. Therefore, you should start with grouping your results first by the product ID, so that the retrieval later via the
Process the product list as the final step
With the above map, it's just a matter of looping through your products and creating an instance of
Your are repeatedly casting
searchResult into a Map, the first suggestion I will make is to cast it once first to make subsequent references easier to read:// use Map if you want to be less strict on the value type,
// at the expense of having to do Object.toString() later
List> searchResultDetails =
(List>) searchResults.getResult();Rationale: Avoid code repetition and makes the one-time cast clearer to understand
Grouping the search results into a usable
MapInstead of traversing your
searchResultMap for each product, consider generating an intermediary Map that gives you the list of CustomerDetails grouped by the product ID:Map> customerDetailsByProduct =
searchResultDetails.stream()
.collect(Collectors.groupingBy(map -> map.get("productID"),
Collectors.mapping(map -> new CustomerDetails(map.get("customerID"),
map.get("appId")), Collectors.toList())));Rationale: Consider when you have \$n\$ products and a result for each of them. Your current solution will effectively be iterating through them, and iterate through the \$n\$ results for the one matching product. Therefore, you should start with grouping your results first by the product ID, so that the retrieval later via the
Map interface will potentially be more efficient.Process the product list as the final step
With the above map, it's just a matter of looping through your products and creating an instance of
ProductInformationWithAssociatedCustomer with the customer details, if present:// BTW do you really need such long class names?
List results =
products.stream()
.map(product -> new ProductInformationWithAssociatedCustomer(product,
customerDetailsByProduct.getOrDefault(product.getId(),
Collections.emptyList())))
.collect(Collectors.toList());Code Snippets
// use Map<String, Object> if you want to be less strict on the value type,
// at the expense of having to do Object.toString() later
List<Map<String, String>> searchResultDetails =
(List<Map<String, String>>) searchResults.getResult();Map<String, List<CustomerDetails>> customerDetailsByProduct =
searchResultDetails.stream()
.collect(Collectors.groupingBy(map -> map.get("productID"),
Collectors.mapping(map -> new CustomerDetails(map.get("customerID"),
map.get("appId")), Collectors.toList())));// BTW do you really need such long class names?
List<ProductInformationWithAssociatedCustomer> results =
products.stream()
.map(product -> new ProductInformationWithAssociatedCustomer(product,
customerDetailsByProduct.getOrDefault(product.getId(),
Collections.emptyList())))
.collect(Collectors.toList());Context
StackExchange Code Review Q#161570, answer score: 2
Revisions (0)
No revisions yet.