Which Java Collection class is better to group the list of objects?
I have a list of messages from users like below:
aaa hi
bbb hello
ccc Gm
aaa Can?
CCC yes
ddd No
From this list of message object I want to count and display aaa(2)+bbb(1)+ccc(2)+ddd(1)
. Any code help?
Putting the pieces together from a couple of the other answers, adapting to your code from the other question and fixing a few trivial errors:
// as you want a sorted list of keys, you should use a TreeMap
Map<String, Integer> stringsWithCount = new TreeMap<>();
for (Message msg : convinfo.messages) {
// where ever your input comes from: turn it into lower case,
// so that "ccc" and "CCC" go for the same counter
String item = msg.userName.toLowerCase();
if (stringsWithCount.containsKey(item)) {
stringsWithCount.put(item, stringsWithCount.get(item) + 1);
} else {
stringsWithCount.put(item, 1);
}
}
String result = stringsWithCount
.entrySet()
.stream()
.map(entry -> entry.getKey() + '(' + entry.getValue() + ')')
.collect(Collectors.joining("+"));
System.out.println(result);
This prints:
aaa(2)+bbb(1)+ccc(2)+ddd(1)
You can use Map<String, Integer>
where the keys represent the individual strings, and the map value is the counter for each one.
So you can do something like:
// where ever your input comes from: turn it into lower case,
// so that "ccc" and "CCC" go for the same counter
String item = userinput.toLowerCase();
// as you want a sorted list of keys, you should use a TreeMap
Map<String, Integer> stringsWithCount = new TreeMap<>();
for (String item : str) {
if (stringsWithCount.contains(item)) {
stringsWithCount.put(item, stringsWithCount.get(item)+1));
} else {
stringsWithCount.put(item, 0);
}
}
And then you can iterate the map when done:
for (Entry<String, Integer> entry : stringsWithCount.entrySet()) {
and build your result string.
That was like the old-school implementation; if you want to be fancy and surprise your teachers, you can go for the Java8/lambda/stream solution.
( where i wouldn't recommend that unless you really invest the time to completely understand the following solution; as this is untested from my side)
Arrays.stream(someListOrArrayContainingItems)
.collect(Collectors
.groupingBy(s -> s, TreeMap::new, Collectors.counting()))
.entrySet()
.stream()
.flatMap(e -> Stream.of(e.getKey(), String.valueOf(e.getValue())))
.collect(Collectors.joining())
You need a MultiSet from guava. That collection type is tailor-made for this kind of task:
MultiSet<String> multiSet = new MultiSet<>();
for (String line : lines) { // somehow you read the lines
multiSet.add(line.split(" ")[0].toLowerCase());
}
boolean first = true;
for (Multiset.Entry<String> entry : multiset.entrySet()) {
if (!first) {
System.out.println("+");
}
first = false;
System.out.print(entry.getElement() + "(" + entry.getCount() + ")");
}
Assuming that you use Java 8, it could be something like this using the Stream API:
List<Message> messages = ...;
// Convert your list as a Stream
// Extract only the login from the Message Object
// Lowercase the login to be able to group ccc and CCC together
// Group by login using TreeMap::new as supplier to sort the result alphabetically
// Convert each entry into login(count)
// Join with a +
String result =
messages.stream()
.map(Message::getLogin)
.map(String::toLowerCase)
.collect(
Collectors.groupingBy(
Function.identity(), TreeMap::new, Collectors.counting()
)
)
.entrySet()
.stream()
.map(entry -> entry.getKey() + '(' + entry.getValue() + ')')
.collect(Collectors.joining("+"))
System.out.println(result);
Output:
aaa(2)+bbb(1)+ccc(2)+ddd(1)
If you want to group your messages by login and have the result as a collection, you can proceed as next:
Map<String, List<Message>> groupedMessages =
messages.stream()
.collect(
Collectors.groupingBy(
message -> message.getLogin().toLowerCase(),
TreeMap::new,
Collectors.toList()
)
);