问题描述
我的问题基本上归结为这个简化的例子。我从数据库返回的数据在行中有一些重复的信息。
My problem essentially comes down to this simplified example. I have data coming back from a DB which has some duplicate information in the rows.
在这个例子中,我有一个从数据库返回的 TeamRow
对象的列表。我可以使用 Collectors.groupingBy
轻松地对这些进行分组:
In this example I have a list of TeamRow
objects that come back from the DB. I can easily group these using Collectors.groupingBy
:
public class TeamRow {
private int id;
private String name;
private String player;
public TeamRow(int id, String name, String player) {
this.id = id;
this.name = name;
this.player = player;
}
public int getId() {return id;}
public String getName() { return name; }
public String getPlayer() {return player;}
}
public class Team {
private int id;
private String name;
private List<String> players;
public Team(int id, String name, List<String> players) {
this.id = id;
this.name = name;
this.players = new ArrayList<String>(players);
}
}
List<TeamRow> dbTeams = new ArrayList<TeamRow>();
dbTeams.add(new TeamRow(1, "Team1", "Jonny"));
dbTeams.add(new TeamRow(1, "Team1", "Rob"));
dbTeams.add(new TeamRow(1, "Team1", "Carlos"));
dbTeams.add(new TeamRow(2, "Team2", "Shane"));
dbTeams.add(new TeamRow(2, "Team2", "Lucas"));
dbTeams.add(new TeamRow(3, "Team3", "Geraint"));
dbTeams.add(new TeamRow(3, "Team3", "Rocky"));
dbTeams.add(new TeamRow(3, "Team3", "Wayne"));
dbTeams.add(new TeamRow(3, "Team3", "Dwayne"));
dbTeams.add(new TeamRow(3, "Team3", "Lester"));
Map<Integer, List<TeamRow>> myMap = dbTeams.stream().collect(Collectors.groupingBy(TeamRow::getId));
然而,我实际上想要实现的是转换 TeamRow
s到团队
s。因此,id和name只表示一次,并且播放器存储在 Team
对象的List中。我可以通过在地图上添加 forEach
来实现这一点,如下所示。
However, what I'm actually trying to achieve is to convert the TeamRow
s to Team
s. So that the id and name are only represented once and the players are stored in a List in the Team
object. I can achieve this by adding a forEach
over the map as shown below.
但我一直在尝试弄清楚是否有一种方法可以通过添加某种映射器或下游收集器来实现相同的结果。这甚至可以提供任何好处,而不是添加后续 forEach
??例如:
But I've been trying to figure out if there is a way I can achieve the same result by adding some sort of mapper or downstream collector. Would this even offer any benefit over adding a subsequent forEach
?? Eg:
List<Team> teams = dbTeams.stream().collect(Collectors.groupingBy(TeamRow::getId, ???), ???).???;
使用forEach进行转换:
Conversion using forEach:
List<Team> teams = new ArrayList<>();
myMap.forEach((id, teamRows) -> {
if (teamRows.size() > 0) {
TeamRow tr = teamRows.get(0);
List<String> players = teamRows.stream().map(TeamRow::getPlayer).collect(Collectors.toList());
teams.add(new Team(id, tr.getName(), players));
}
});
推荐答案
您可以使用收集器。将 merge
方法添加到团队
类可能是个好主意:
You may use toMap
collector with custom merge function. It's probably a good idea to add merge
method to the Team
class:
public class Team {
private final int id;
private final String name;
private final List<String> players;
public Team(int id, String name, List<String> players) {
this.id = id;
this.name = name;
this.players = new ArrayList<>(players);
}
// merges other team into this team, returning this team
public Team merge(Team other) {
assert id == other.id; // remove asserts if you don't like them
assert name.equals(other.name);
players.addAll(other.players);
return this;
}
}
现在你可以通过这种方式解决问题:
Now you can solve your problem this way:
Collection<Team> teams = dbTeams.stream()
.map(tr -> new Team(tr.id, tr.name, Arrays.asList(tr.player)))
.collect(Collectors.toMap(t -> t.id, t -> t, Team::merge)).values();
这篇关于如何使用Java 8 Streams groupingBy将多个数据库记录映射到具有List< String>的单个对象一栏的财产的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!