I have a list of beans (List 1) and I want to convert it to another list (List 2). I did that. But I have the impression it made it too complicated : pick out the id's loop stream again. My question is : is it possible to make it simpler ?
[Customer [id=1, name=jon, carType=fiat, color=black], Customer [id=2, name=din, carType=ford, color=yellow], Customer [id=1, name=jon, carType=benz, color=white], Customer [id=1, name=jon, carType=volvo, color=red], Customer [id=3, name=fin, carType=volvo, color=black], Customer [id=3, name=fin, carType=fiat, color=green], Customer [id=4, name=lara, carType=bmw, color=red], Customer [id=5, name=tina, carType=toyota, color=white], Customer [id=5, name=tina, carType=fiat, color=yelow], Customer [id=6, name=bogi, carType=benz, color=black]]
ImprovedCustomer [id=1, name=jon, cars=[Car [carType=fiat, color=black], Car [carType=benz, color=white], Car [carType=volvo, color=red]]] ImprovedCustomer [id=2, name=din, cars=[Car [carType=ford, color=yellow]]] ImprovedCustomer [id=3, name=fin, cars=[Car [carType=volvo, color=black], Car [carType=fiat, color=green]]] ImprovedCustomer [id=4, name=lara, cars=[Car [carType=bmw, color=red]]] ImprovedCustomer [id=5, name=tina, cars=[Car [carType=toyota, color=white], Car [carType=fiat, color=yelow]]] ImprovedCustomer [id=6, name=bogi, cars=[Car [carType=benz, color=black]]]
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class App {
public static void main(String[] args) {
List<Customer> list = new ArrayList<>();
list.add(new Customer(1, "jon", "fiat", "black"));
list.add(new Customer(2, "din", "ford", "yellow"));
list.add(new Customer(1, "jon", "benz", "white"));
list.add(new Customer(1, "jon", "volvo", "red"));
list.add(new Customer(3, "fin", "volvo", "black"));
list.add(new Customer(3, "fin", "fiat", "green"));
list.add(new Customer(4, "lara", "bmw", "red"));
list.add(new Customer(5, "tina", "toyota", "white"));
list.add(new Customer(5, "tina", "fiat", "yelow"));
list.add(new Customer(6, "bogi", "benz", "black"));
List<Integer> ids = list.stream().map(Customer::getId).distinct().collect(Collectors.toList());
List<ImprovedCustomer> ImprovedCustomers = new ArrayList<>();
for (int id : ids) {
ImprovedCustomer improvedCustomer = new ImprovedCustomer(id);
List<Car> collect = list.stream().filter(a -> a.getId().equals(id)).map(a -> {
if (improvedCustomer.getName() == null) {
improvedCustomer.setName(a.getName());
}
return new Car(a.getCarType(), a.getColor());
}).collect(Collectors.toList());
improvedCustomer.setCars(collect);
ImprovedCustomers.add(improvedCustomer);
}
}
}
class Customer {
private Integer id;
private String name;
private String carType;
private String color;
public Customer(Integer id, String name, String carType, String color) {
super();
this.id = id;
this.name = name;
this.carType = carType;
this.color = color;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCarType() {
return carType;
}
public void setCarType(String carType) {
this.carType = carType;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
class Car {
private String carType;
private String color;
public Car(String carType, String color) {
super();
this.carType = carType;
this.color = color;
}
public String getCarType() {
return carType;
}
public void setCarType(String carType) {
this.carType = carType;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
class ImprovedCustomer {
private Integer id;
private String name;
private List<Car> cars;
public ImprovedCustomer() {
super();
}
public ImprovedCustomer(Integer id) {
super();
this.id = id;
}
public ImprovedCustomer(Integer id, String name, List<Car> cars) {
super();
this.id = id;
this.name = name;
this.cars = cars;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}
}
CodePudding user response:
Your current solution performs an iteration over the list of customers for every unique id
.
As @Johannes Kuhn has pointed out in the comments, you can eliminate redundant iterations by making use of the collector groupingBy(classifier, downstream)
. With this collector, we can map each customer with unique id
to a list of cars in a single iteration.
And then construct a list of ImprovedCustomer
objects based on every entry of the map generated by groupingBy()
.
Note: that in order to use this approach, either Customer
class should implement equals/hashCode
contract based on the id
and name
properties, or we need to use an auxiliary record (or class) that would wrap a Customer
object. For simplicity, I'll assume that we can rely on the Customer
class equals/hashCode
implementations.
So, in the code below as classifier function I've applied Function.identity()
(i.e. a key would be a customer itself) and as downstream collector a combination of collectors mapping()
and toList()
, which transforms every customer into a Car
object, and collect the cars mapped to the same customer into a list.
public static void main(String[] args) {
List<Customer> customers = // initializing the list of customers
List<ImprovedCustomer> improvedCustomers = customers.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.mapping(cust -> new Car(cust.getCarType(), cust.getColor()),
Collectors.toList())
))
.entrySet().stream()
.map(entry -> new ImprovedCustomer(entry.getKey().getId(), entry.getKey().getName(), entry.getValue()))
.collect(Collectors.toList());
}