I have a HashMap of teams that I need to print out. They need to be indexed and sorted by some parameters(not relevant). Everything works fine except for the indexing part which I got working in a way but it doesn't feel right.
int i=0;
public void printTable()
{
StringBuilder sb = new StringBuilder();
i = 0;
table.keySet().stream()
.map(key -> table.get(key))
.sorted(Comparator.comparing(Team::getPoints).thenComparing(Team::goalDifference).reversed().thenComparing(Team::getName))
.forEach(team -> sb.append(String.format("%2d%s %-15s%5d%5d%5d%5d%5d\n", ++i, ".", team.getName(), team.getPlayed(),
team.getWins(), team.getDraws(), team.getLosses(), team.getPoints())));
System.out.print(sb.toString());
}
Is there a better way of doing this? I don't think Intstream would help here because I wouldn't be able to get the object from the HashMap using an index.
You should avoid stateful streams / lambda expressions. reference . Due to this reason, my original answer below is a bad idea.
A better approach is to separate stream and printing.
List<Team> teams = table.values()
.stream()
.sorted(...)
.collect(Collectors.toList());
StringBuilder sb = new StringBuilder();
for(int i=0; i<teams.size(); i++) {
Team team = teams.get(i);
sb.append(String.format("%2d. %-15s%5d%5d%5d%5d%5d\n",
i,
team.getName(),
team.getPlayed(),
team.getWins(),
team.getDraws(),
team.getLosses(),
team.getPoints()));
}
System.out.println(sb.toString());
You cannot modify the reference of the variables/modify primitives that you are used within a stream or lambda. You can rewrite your code like below,
AtomicInteger i = new AtomicInteger();
String result = table.values()
.stream()
.sorted(...)
.map(e -> i.incrementAndGet() + "_" + e)
.collect(Collectors.joining());
AtomicInteger
will give you mutability without changing the reference/modifying the primitive. You don't need to do look-ups in the map, you can directly iterate over values.
Edit
As @Eugene pointed out in the comment, the approach with AtomicInteger
cannot be used if you are using parallel stream.
I would separate the sorting from the printing.
Sorting:
List<Team> sorted = table.values().stream()
.sorted(Comparator.comparing(Team::getPoints)
.thenComparing(Team::goalDifference)
.reversed()
.thenComparing(Team::getName))
.collect(Collectors.toList());
Printing:
String table = IntStream.range(0, sorted.size())
.mapToObj(i -> String.format("%2d%s %-15s%5d%5d%5d%5d%5d",
i, ".",
sorted.get(i).getName(),
sorted.get(i).getPlayed(),
sorted.get(i).getWins(),
sorted.get(i).getDraws(),
sorted.get(i).getLosses(),
sorted.get(i).getPoints()))
.collect(Collectors.joining("\n"));
This could be improved if the Team
class had a method that accepts the position and returns the formatted string:
public String formatWithPosition(int position) {
return String.format("%2d%s %-15s%5d%5d%5d%5d%5d",
position, ".",
name, played, wins, draws, losses, points);
}
Then, you could use it as follows:
String table = IntStream.range(0, sorted.size())
.mapToObj(Team::formatWithPosition)
.collect(Collectors.joining("\n"));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.