2

Here is the scenario, in my source list, it contains all the Users object. Each user object will have id, event, and timestamp. I need to create a destination list to contains all the user object that which have latest timestamp record for each id. Like example below

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.vincent.object.User;

public class Test {
  public static void main(String[] args) throws Exception {
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    User u1 = new User("1", "55", dateFormat.parse("2017-10-01 10:11:01.111"));
    User u2 = new User("1", "105", dateFormat.parse("2017-10-01 10:11:02.111"));
    User u3 = new User("2", "55", dateFormat.parse("2017-10-01 10:11:03.111"));
    List<User> sources = new ArrayList<>();
    sources.add(u1);
    sources.add(u2);
    sources.add(u3);

    List<User> destination = new ArrayList<>();
    // I want my destination array only contains following 2 result:
    // u2 and u3 from the source
  }
}

How can I approach this?

EDIT: Here is the User class

import java.util.Date;

public class User {
  private String id;
  private String reason;
  private Date date;

  public User(String id, String reason, Date date) {
    super();
    this.id = id;
    this.reason = reason;
    this.date = date;
  }
  // getter setter
}
Vincent Zheng
  • 385
  • 1
  • 4
  • 19
  • If I read the question, the latest date of user "1", and user "2". Some stream operation? – M. le Rutte Oct 12 '17 at 15:34
  • Hi Vincent do you mean hold one list in another ?Can't you just store source list in destination after adding and add a condition to check whether contents of the list a re same by using !Collections.disjoint(list1, list2); or normal equals method list1.equals(list2); – Pradeep Oct 12 '17 at 15:36
  • Here is a question with stream that uses a stateful filter predicate: https://stackoverflow.com/questions/23699371/java-8-distinct-by-property – M. le Rutte Oct 12 '17 at 15:39
  • FYI, `Date` and `SimpleDateFormat` are part of the troublesome old date-time classes that are now legacy, supplanted by the java.time classes. – Basil Bourque Oct 12 '17 at 16:21

2 Answers2

3

You can use Java 8 streams for this.

public class Test {
    public static void main(String[] args) throws Exception {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        User u1 = new User("1", "55", dateFormat.parse("2017-10-01 10:11:01.111"));
        User u2 = new User("1", "105", dateFormat.parse("2017-10-01 10:11:02.111"));
        User u3 = new User("2", "55", dateFormat.parse("2017-10-01 10:11:03.111"));
        User u4 = new User("2", "105", dateFormat.parse("2017-10-01 10:11:04.111"));
        List<User> sources = new ArrayList<>();
        sources.add(u1);
        sources.add(u2);
        sources.add(u3);
        sources.add(u4);


        List<User> destination = sources.stream()
                .collect(Collectors.groupingBy(User::getId, Collectors.maxBy(Comparator.comparing(User::getDate))))
                .values()
                    .stream()
                    .map(o -> o.get())
                    .collect(Collectors.toList());

        System.out.println(destination);
    }
}

Here I'm gruping by Ids and getting the user with the max date.

Output (obviously overriding toString on User class):

[User{id='1', reason='105', date=Sun Oct 01 10:11:02 BOT 2017},
User{id='2', reason='105', date=Sun Oct 01 10:11:04 BOT 2017}]

Juan Carlos Mendoza
  • 5,736
  • 7
  • 25
  • 50
0

I'm assuming you don't need to keep the order of the users as it was in the source list. The idea is the following:

  • iterate over the source list
  • add each user in a map if it was not present, update the user if the one stored had a timestamp lower than the current one (assuming that two users are equals if they have the same id)
  • return the key set of the map.

    public static void main(String[] args)
    {
         DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
         User u1 = new User("1", "55", dateFormat.parse("2017-10-01 10:11:01.111"));
         User u2 = new User("1", "105", dateFormat.parse("2017-10-01 10:11:02.111"));
         User u3 = new User("2", "55", dateFormat.parse("2017-10-01 10:11:03.111"));
         List<User> sources = new ArrayList<>();
         sources.add(u1);
         sources.add(u2);
         sources.add(u3);
    
         Set<User> uniqUsers = new HashSet<>();
         for(User u : sources)
         {
             if(uniqUsers.containsKey(u))
             {
                 User oldUser = uniqUsers.get(u);
                 long oldTimeStamp = uniqUsers.get(u).getDate().getTime();
                 long currentTimeStamp = u.getDate().getTime();
                 if(currentTimeStamp > oldTimeStamp)
                     uniqUsers.remove(oldUser);
                uniqUsers.add(u);
             }
          }
          List<User> destination = new ArrayList<>(uniqUsers);
    
     }
    

The User class must be modified in this way (in order to work correctly with the HashSet.

public class User 
{
    private String id;
    private String reason;
    private Date date;

    public User(String id, String reason, Date date) 
    {
        super();
        this.id = id;
        this.reason = reason;
        this.date = date;
     }
     // getter setter


     @Override
     public int hashCode()
     {
        return id.hashCode();
     }

     @Override
     public boolean equals(Object obj)
     {
         return obj instanceof User && ((User) obj).id.equals(id);
     }
}
Tommaso Pasini
  • 1,521
  • 2
  • 12
  • 16
  • if you want to use streams as suggested in some comments you can always applying a filter that implement the HashSet logic. Another solution could be to sort the array by date (`Collections.sort(sources, Comparator.comparingInt(u -> u.getDate().getTime())`); and then iterating from right to left keep each user only the first time you meet him. – Tommaso Pasini Oct 12 '17 at 15:52