Search code examples
lambdajava-8attributesjava-streammax

Java8 lambda stream remove objects with max in two attribute of objects


I have List of objects any objects have two Date properties insertDate and updateDate I need to find the object that have absolute max between the two properties insertDate and updateDate. Example (Object of the list have properties):

-> insertDate = 2020-10-11 15:48 updateDate = 2020-11-11 15:48
   insertDate = null             updateDate = 2019-09-11 15:48
   insertDate = 2019-10-11 15:48 updateDate = null
   insertDate = null             updateDate = null
   insertDate = 2019-10-11 15:48 updateDate = 2019-11-11 10:48

So the arrow indicated the object of the List which will be removed. I tried different approach:

  1. Using stream using sort(...).reverse to get the max to remove from insertDate attribute and from updateDate attribute then compare the two values to find max and use the result object to remove but it too complicate and have low performances
  2. I tried to use groupingBy of Collections.... but I can't found a mode to remove based on max on two attribute

etc etc..

Important: consider that the Date attribute where we search max can be null

Can somebody have a performance and elegant solution? It's too complicate and I'm not very speedly with lambda ....

Thanks a lot for your support


Solution

  • Here is one possibility. I did this using LocalDateTime, not Date because Date is outdated. The java::time package should be used. I updated my version of your class for demonstration purposes. You will need to subsitute the name of your class with MyObject for this to work with your data.

    The Data.

    List<MyObject> list = List.of(
            new MyObject("2020-10-11 15:48", "2020-11-11 15:48"),
            new MyObject(null, "2019-09-11 15:48"),
            new MyObject("2019-10-11 15:48", null),
            new MyObject(null, null),
            new MyObject("2019-10-11 15:48", "2019-11-11 10:48"));
    

    The Comparator.

    • this works by getting the duration of each objects date pair, converting to millseconds and then comparing each of those as long values.
    Comparator<MyObject> comp = (a,b)-> 
    Long.compare(Duration.between(a.getInsertDate(),a.getUpdateDate())
            .toMillis(),
            Duration.between(b.getInsertDate(),b.getUpdateDate())
            .toMillis());
    

    Now simply stream the list, filter out objects with nulls for any of the times. The result returns the object with maximum duration. If nothing is found, it returns a null.

    MyObject  result = list.stream()
            .filter(obj -> obj.getInsertDate() != null
                    && obj.getUpdateDate() != null)
            .max(comp).orElse(null);
    
    System.out.println(result);
    

    This prints

    2020-10-11 15:48 2020-11-11 15:48
    

    For completon I provided a Date comparator to use since you have Date fields in your object. Just substitute the dateComparator for comp in the previous stream and it should work just fine.

    Comparator<MyObject> dateComparator = (a, b) -> Long.compare(
            Math.abs(a.getInsertDate().toInstant().toEpochMilli()-
                            a.getUpdateDate().toInstant().toEpochMilli()),                  
            Math.abs(b.getInsertDate().toInstant().toEpochMilli()-
                    b.getUpdateDate().toInstant().toEpochMilli()));
    

    The object class definition.

    class MyObject {
        public LocalDateTime insertDate;
        public LocalDateTime updateDate;
        
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        public MyObject(String insertDate, String updateDate) {
            if (insertDate != null) {
                this.insertDate = LocalDateTime.parse(insertDate,dtf);
            }
            if (updateDate != null) {
                this.updateDate = LocalDateTime.parse(updateDate,dtf);
            }
        }
        
        public LocalDateTime getInsertDate() {
            return insertDate;
        }
        
        public LocalDateTime getUpdateDate() {
            return updateDate;
        }
        
        public String toString() {
            return insertDate.format(dtf) + " " + updateDate.format(dtf);
        }
    }