Сравнение нескольких полей ArrayList с использованием Lambda API и сбор объектов - PullRequest
4 голосов
/ 05 марта 2020

Я столкнулся с ситуацией, аналогичной описанной ниже в моем проекте, из-за которой я не могу реализовать код.

У меня есть класс POJO

    public class TranObject {

        public String loadId;
        public String vDate;
        public String dDate;
        public String pDate;

        public TranObject(String loadId, String vDate, String dDate, String pDate) {
            super();
            this.loadId = loadId;
            this.vDate = vDate;
            this.dDate = dDate;
            this.pDate = pDate;
        }
      //Getter and Setters
      //toString()

 }

Теперь у меня есть другой класс процессора, в котором я хочу реализовать некоторое сравнение между объектами перегрузки, которые я получаю через вызов службы данных, и собирать их в другой коллекция. Лог реализации c приведен в комментариях ниже. Пожалуйста, прочитайте комментарии ниже

import java.util.Arrays;
import java.util.List;

public class DemoClass {

    public static void main(String[] args) {
        List<TranObject> listObj = Arrays.asList(
                new TranObject("LOAD1", "20180102", "20180202", null),
                new TranObject("LOAD2", "20180402", "20180403", null),
                new TranObject("LOAD3", "20180102", "20180202", "20190302"),
                new TranObject("LOAD4", "20180402", "20180403", null),
                new TranObject("LOAD5", "20200202", "20200203", null)
        );

        /*
        IF (obj1, obj3 vdate and dDate are equal)
                IF(pDate == null for obj1 or obj3)
                    THEN obj1 and obj3 are equal/duplicates, and we collect them.
                ELSE IF(pDate != null for obj1 and obj3)
                    IF(pDate is same for obj1 and obj3)
                        THEN obj1 and obj3 are duplicates, and we collect them.
                    ELSE
                        THEN obj1 and obj3 are unique.
         */
    }
}

Мой конечный результат должен представлять собой коллекцию типа List, содержащую дубликаты объектов Tran, для дальнейшего обновления.

Я искал inte rnet, чтобы узнать, как чтобы решить это с помощью Lambda API. -> Попробовал сначала использовать groupingBy с vDate, а затем с dDate, но потом я не смог сравнить их на равенство pDate.

Может кто-нибудь помочь мне решить эту проблему. Небольшая помощь будет очень полезна для меня. Я застрял здесь

ОБНОВЛЕНИЕ: После некоторого чтения я пытаюсь реализовать то же самое путем переопределения метода equals в классе POJO, как показано ниже:

@Override
    public boolean equals(Object obj) {
        boolean isEqual=false;
        if(obj!=null) {
            TranObject tran = (TranObject) obj;
            isEqual=(this.vDate.equals(tran.getvDate()) && this.dDate.equals(tran.getdDate()));
            if(isEqual && this.pDate != null && tran.getpDate()!= null) {
                isEqual = (this.pDate.equals(tran.getpDate()));
            }
        }
        return isEqual;
    }

Тем не менее это не работает, как ожидалось ... Может кто-нибудь, пожалуйста, помогите мне, почему ??

Ответы [ 2 ]

2 голосов
/ 05 марта 2020

Наиболее близким к вашему требованию будет группирование во вложенном виде, а затем фильтрация внутреннего Map для различных условий, в конечном итоге интересующихся только значениями.

Stream<Map<String, List<TranObject>>> groupedNestedStream = listObj.stream()
        .collect(Collectors.groupingBy(a -> Arrays.asList(a.vDate, a.dDate)
                , Collectors.groupingBy(t -> t.pDate == null ? "default" : t.pDate)))
        .values().stream();

из этих групп дополнительно условия для допустимые значения (из карты)

  1. все они имеют одинаковые pDate, в этом случае innerMap будет иметь только одну запись с общим pDate (m.size() == 1)
  2. одно из значений после группировки имеет ровно один pDate как null (что означает m.containsKey("default") && m.get("default").size() == 1)
List<TranObject> tranObjects = groupedNestedStream
        .filter(m -> m.size() == 1 || (m.containsKey("default") && m.get("default").size() == 1))
        .flatMap(m -> m.values().stream().flatMap(List::stream))
        .collect(Collectors.toList());

Обратите внимание, что использование строковой константы "default" позволяет избежать сбоев (или плохая практика) при сборе Map с null ключами или значениями.

0 голосов
/ 05 марта 2020

Похоже, что TranObject нужен метод equals и hashCode.

@Override
public boolean equals(Object obj) {
  //check instanceof and self comparison

  TranObject other = (TranObject) obj;


  if(this.vDate.equals(other.vDate) && this.dDate.equals(other.dDate)) {
    //if pDate is not given then consider them duplicate
    if(this.pDate == null || other.pDate == null)
      return true;

    //if pDate are the same then they are duplicate, otherwise they are unique
    return this.pDate.equals(other.pDate);
  }

return false;

}

//auto generated by Eclipse
@Override
public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((dDate == null) ? 0 : dDate.hashCode());
  result = prime * result + ((pDate == null) ? 0 : pDate.hashCode());
  result = prime * result + ((vDate == null) ? 0 : vDate.hashCode());
  return result;
}

Теперь, когда у вас есть метод равенства, чтобы определить, считаются ли два TranObjects равными (на основе указанных вами правил), просто соберите элементы, которые встречаются в списке более одного раза:

private static List<TranObject> collectDuplicates(List<TranObject> list) {
  List<TranObject> result = new ArrayList<TranObject>();

  for(TranObject element : list) {
    if(Collections.frequency(list, element) > 1) 
      result.add(element);
  }

  return result;

}

Это вернет все элементы, которые имеют дубликаты.

Примечание: collectDuplicates не возвращает уникальный список дублирующихся элементов. Вместо этого он возвращает список каждого дублированного элемента (как того требует вопрос OP).

...