There is a class User, which has its ID, name, number of sent messages and the date when these messages were sent.

Objects in the collection come from a database of this type:

ID Name Сообщения Дата 1 test3 300 2018-11-02 2 test2 228 2018-11-02 3 test3 124 2018-11-05 4 test4 24 2018-11-03 5 test3 242 2018-11-01 6 test6 334 2018-11-03 7 test7 32 2018-11-03 8 test8 22 2018-11-03 9 test9 245 2018-11-03 10 test10 145 2018-11-03 

Where each line is one item in List <User>

The problem is that the records in the table are duplicated, only the date and number of messages change, and the name remains the same. And, if I want to receive message statistics for a certain period, then I will receive many different objects with the same Name. For example, in the List created according to the table above the objects with Name test3 already 3 pieces, but I would like to have one, but with the number of messages of all three.

Question: How can I delete from the List all objects that have the same Name, except for one, and add the number of deleted objects to its number of messages?

  • perhaps it makes sense to write separately getting users with unique names, then nothing will need to be filtered on the Java side, or if you need only their number, then return it - keekkenen

2 answers 2

I do not know in what form the date is stored and what the logic of its reduction should be, therefore I do not take it into account in the example.

 class User { private String name; private int messages; public User(String name, int messages) { this.name = name; this.messages = messages; } public User(User another) { this.name = another.name; this.messages = another.messages; } public String getName() { return name; } public int getMessages() { return messages; } public User merge(User another) { this.messages += another.messages; return this; } public String toString() { return String.format("%s: %d", name, messages); } } List<User> merged = users.stream() .collect(Collectors.collectingAndThen( Collectors.toMap( User::getName, User::new, User::merge), m -> new ArrayList<>(m.values()))); 

Naturally, you can do without defining additional methods in the User class, by defining them as lambdas in the collector

 Collectors.toMap( User::getName, u -> new User(u.getName(), u.getMessage()), (a, b) -> { a.setMessages(a.getMessages() + b.getMessages()); return a; }) 

But this is obviously less pure code.

    If I understand you correctly, then something like this:

     public class Data { private int id; private String name; private String massage; private LocalDate localDate; public Data(int id, String name, String massage, LocalDate localDate) { this.id = id; this.name = name; this.massage = massage; this.localDate = localDate; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getMassage() { return massage; } public void setMassage(String massage) { this.massage = massage; } public LocalDate getLocalDate() { return localDate; } public void setLocalDate(LocalDate localDate) { this.localDate = localDate; } } public class Application { public static void main(String[] args) { List<Data> dataList = new ArrayList<>(asList( new Data(1, "name1", "message1", LocalDate.now()), new Data(2, "name2", "message2", LocalDate.now()), new Data(3, "name2", "message3", LocalDate.now()), new Data(4, "name3", "message4", LocalDate.now()), new Data(5, "name3", "message5", LocalDate.now()), new Data(6, "name3", "message6", LocalDate.now()) )); Map<String, Long> dataMap = dataList.stream() .collect(groupingBy(Data::getName, counting())); dataMap.forEach((key, value) -> System.out.println("key: " + key + " -> value: " + value)); } }