简体   繁体   中英

grouping List of Objects and counting using Java collection

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?

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()
            )
        );

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)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM