【问题标题】:Sorting list of objects based on nested values in java基于Java中的嵌套值对对象列表进行排序
【发布时间】:2019-10-10 06:50:53
【问题描述】:

我有一份计划在多个日期交付的汽车清单,其中 需要根据以下几点进行排序:

  • 如果是isReady>0,,那么它应该首先显示在表格中。和 然后其他值在该特定日期低于它。
  • 如果isReady>0和Object gear!=null那么,它首先显示在 该特定日期的表格。后跟对象gear==null 的其他值。
  • 如果isReady>0、对象gear!=null和对象tyre!=null,那么 该特定日期的值首先显示在表格中。后面是 Object gear==nulltyre==null 的其他值。

这里是class 代码:

public class Car {
    private int isReady;
    private Tyre tyre;
    private Gear gear;
    private Date deliveryDate;
}


public class Gear {
    private int id;
    private String type;
}


public class Tyre {
    private int id;
    private String grip;
}

public class CarComparator implements Comparator<Car> {
    @Override
    public int compare(Car entry1, Car entry2) {
        int value = 0;

        if (entry1.getIsReady() > entry2.getIsReady()) {
            value = -1;
        } else if (entry1.getIsReady() < entry2.getIsReady()) {
            value = 1;
        } else if (entry1.getIsReady() == entry2.getIsReady()) {
            value = 0;
        }
        return value;
    }
}

我开发了一个比较器,它适用于第一个条件 其中isReady&gt;0。你能帮我解决其他条件吗 上面提到过。

提前致谢。

【问题讨论】:

  • 在你当前的代码中,如果value结尾不是0,你可以返回。但如果是0,则需要再比较下一个条件。以此类推。
  • 旁注:您当前的比较器可以简化为int value =Integer.compare(entry2.getIsReady(), entry1.getIsReady() );
  • 当我的 value=0 时,我需要创建一个函数,例如 compareTyre() 并检查其中的条件?
  • @champ.exe 您不一定需要将其设为单独的方法,但您可以。但是如果 value 是0 并且你还有东西要比较,你需要做更多的比较。
  • 您对isReady&gt;0 的要求与您的比较器不匹配。澄清一下:你有一个列表中的所有汽车,并希望对它们进行排序 1)按日期升序,2)按 isReady 升序,3)非空值首先为 gear 和 4)非空tyre 的第一个值 - 正确吗?日期相同的汽车,isReadygeartyre 应该如何排序?有关系吗?

标签: java sorting collections


【解决方案1】:

嗯,从 Java 8 开始,您可以像这样构建比较器:

//order by delivery date first, ascending order
Comparator<Car> carComparator = Comparator.comparing( Car::getDeliveryDate )
  //order by isReady in ascending order
  .thenComparing( Car::getIsReady )
  //we map null to 1 and non-null to -1 and ignore the rest for now
  .thenComparing( car -> car.getGear() != null ? -1 : 1 ) 
  .thenComparing( car -> car.getTyre() != null ? -1 : 1 );

【讨论】:

    【解决方案2】:

    检查此比较器,以便您可以使用多个属性进行排序

    public class CarComparator implements Comparator<Car> {
    
        @Override
        public int compare(Car entry1, Car entry2) {
            int value;
            if (entry1.getDeliveryDate().before(entry2.getDeliveryDate())){
                value = -1;
            }else if (entry1.getDeliveryDate().equals(entry2.getDeliveryDate())){
                value = 0;
            }else{
                value =1;
            }
            //For same day
            if (value==0){
                if (entry1.getIsReady() > entry2.getIsReady()) {
                    value = -1;
                } else if (entry1.getIsReady() < entry2.getIsReady()) {
                    value = 1;
                } else if (entry1.getIsReady() == entry2.getIsReady()) {
                    value = 0;
                }
            }
            //if same isReady
            if (value==0){
                if (entry1.getGear()!=null && entry2.getGear()==null) {
                    value = -1;
                } else  if (entry1.getGear()==null && entry2.getGear()==null) {
                    value = 0;
                } else{
                    value = 1;
                }
            }
            //if still equals
            if (value==0){
                if (entry1.getTyre()!=null && entry2.getTyre()==null) {
                    value = -1;
                } else  if (entry1.getTyre()==null && entry2.getTyre()==null) {
                    value = 0;
                } else{
                    value = 1;
                }
            }
    
    
            return value;
        }
    }
    

    我不确定这是否是您尝试做的。上述比较器的作用是: 首先使用日期排序,如果找到相等的日期(值=0),它会比较 isReady,然后是 getGear(),最后是 getTyre()。

    这样您就可以在比较器中添加任意数量的属性。

    包括3辆车的主要方法

    public class Main {
        public static void main (String[]args) throws UnsupportedEncodingException, ParseException {
    
            List<Car> carL = new ArrayList<Car>();
    
            Car car1 = new Car();
            car1.setDeliveryDate(new Date());
            Gear gear1 = new Gear();
            car1.setGear(gear1);
            Tyre tyre1 = new Tyre();
            car1.setTyre(null);
            car1.setId(1);
            car1.setDeliveryDate((new SimpleDateFormat("dd-MM-yyyy")).parse("01-01-2000"));
            car1.setIsReady(0);
    
            Car car2 = new Car();
            car2.setDeliveryDate(new Date());
            Gear gear2 = new Gear();
            car2.setGear(gear2);
            Tyre tyre2 = new Tyre();
            car2.setTyre(tyre2);
            car2.setId(2);
            car2.setDeliveryDate((new SimpleDateFormat("dd-MM-yyyy")).parse("02-01-2000"));
    
            car2.setIsReady(1);
    
            Car car3 = new Car();
            car3.setDeliveryDate(new Date());
            Gear gear3 = new Gear();
            car3.setGear(gear3);
            Tyre tyre3 = new Tyre();
            car3.setTyre(tyre3);
            car3.setId(3);
            car3.setDeliveryDate((new SimpleDateFormat("dd-MM-yyyy")).parse("01-01-2000"));
    
            car3.setIsReady(1);
    
            carL.add(car1);
            carL.add(car2);
            carL.add(car3);
            Collections.sort(carL, new CarComparator());
            for (Car car : carL) {
                System.out.println("car: " + car.toString());
            }
        }
    }
    

    输出:

    car: Car{id=3, isReady=1, tyre=false, gear=false, deliveryDate=Sat Jan 01 00:00:00 EET 2000}
    car: Car{id=1, isReady=0, tyre=true, gear=false, deliveryDate=Sat Jan 01 00:00:00 EET 2000}
    car: Car{id=2, isReady=1, tyre=false, gear=false, deliveryDate=Sun Jan 02 00:00:00 EET 2000}
    

    【讨论】:

      【解决方案3】:

      老实说,我认为您的代码没有任何问题。也就是说,如果您的意图是在 entry1 大于 entry 2 时返回 -1(规范的相反顺序)。无需阅读更多代码,如果您尝试比较为您的用例构建的哪个值更大或更小,我认为您的代码将起作用。

      但是,我认为您的退货方法效率低下。您不需要返回值。您可以只返回一个实际值。

      为了比较,您可以先比较 ==,然后再评估其余部分。但这可能很难阅读代码,所以我给你两个版本。

      删除值版本:

      public class CarComparator implements Comparator<Car> {
          @Override
          public int compare(Car entry1, Car entry2) {
              if (entry1.getisReady() > entry2.getisReady()) {
                  return -1;
              } else if (entry1.getisReady() < entry2.getisReady()) {
                  return 1;
              } else if (entry1.getisReady() == entry2.getisReady()) {
                  return 0;
              }
          }
      }
      

      移除值和不同风格的比较:

      public class CarComparator implements Comparator<Car> {
      
          @Override
          public int compare(Car entry1, Car entry2) {
              if (entry1.getisReady() == entry2.getisReady()) return 0;            
              return entry1.getisReady() > entry2.getisReady()? -1 : 1;
          }
      }
      

      我不确定我是否正确地理解了你,但如果没有,我希望这段代码可以帮助你。我将它们分成 3 种方法,我希望你有一个 getTyre() 和 getGear() 方法。您可以根据需要将它们组合起来,对于最后一个方法,它的值被分成变量顺序以便于代码阅读。

      class CarComparator implements Comparator<Car> {
      
          public int compare(Car entry1, Car entry2) {
              if (entry1.getisReady() == entry2.getisReady()) return 0;
              return entry1.getisReady() > entry2.getisReady()? -1 : 1;
          }
      
          public int compareGear(Car entry1, Car entry2){
              if ( (entry1.getGear() != null && entry2.getGear() != null) 
                 ||(entry1.getGear() == null && entry2.getGear() == null)
                 ){
                  return compare(entry1, entry2);
              }  
              return entry1.getGear() != null && entry2.getGear() == null? -1 : 1;
      
          }
      
          public int compareTye(Car entry1, Car entry2){
              int order1 = entry1.getGear() != null && entry1.getTyre() != null? 1 : 0;
              int order2 = entry2.getGear() != null && entry2.getTyre() != null? 1 : 0;
      
              if ( order1 == order2 ) return compare(entry1, entry2);
              return order1 > order2? -1 : 1;
          } 
      }
      

      【讨论】:

      • 感谢您的简化。我的代码工作正常,但我需要对象的嵌套排序帮助。这适用于tyregear
      • 不客气。我不确定我是否正确理解了您,但我更新了答案。我希望这会有所帮助。您可以随时组合这些方法。您还可以编写轮胎,以便 compareTyre 调用 compareGear() 然后 compareGear 调用 compare 或其他方式。
      • 谢谢,我会尝试执行它,看看它是否有效。
      【解决方案4】:

      为什么不重用 Integer.compareTo 来缩短代码?

      像这样:

      import java.util.Comparator;
      
      public class CarComparator implements Comparator<Car> {
          @Override
          public int compare(Car entry1, Car entry2) {
              int value = 0;
      
              // might want to add a null check for either entry1 and entry2
      
              value = entry1.getDeliveryDate().compareTo(entry2.getDeliveryDate());
              if (value == 0) {
                  value = ((Integer)entry1.getIsReady()).compareTo((Integer)entry2.getIsReady());
                  if (value == 0) {
                      value = getIntegerValueForNullCheck(entry1.getGear()).compareTo(getIntegerValueForNullCheck(entry2.getGear()));
                      if (value == 0) {
                          value = getIntegerValueForNullCheck(entry1.getTyre()).compareTo(getIntegerValueForNullCheck(entry2.getTyre()));
                      }
                  }
              }
      
              return value;
          }
          private Integer getIntegerValueForNullCheck (Object o) {
              return o == null ? 0 : 1;
          }
      }
      

      包括测试排序的代码:

      import java.time.Duration;
      import java.time.Instant;
      import java.time.LocalDateTime;
      import java.util.*;
      
      public class Sorting {
      
          public static void main(String[] args) {
      
              List<Car> cars = new LinkedList<>();
      
      
              Date today = new Date();
              Instant now = Instant.now();
              Instant after = now.plus(Duration.ofDays(1));
              Date tomorrow = Date.from(after);
      
              cars.add(new Car(5, new Tyre(1,"1"), new Gear(1, "1"), today ));
              cars.add(new Car(5, new Tyre(1,"1"), null, today ));
              cars.add(new Car(5, null, null, today ));
              cars.add(new Car(4, null, null, today ));
              cars.add(new Car(3, null, null, tomorrow ));
      
      
              Collections.sort(cars, new CarComparator());
              System.out.println(cars);
          }
      
      }
      

      输出:

      [Car{isReady=4, tyre=null, gear=null, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
      , Car{isReady=5, tyre=null, gear=null, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
      , Car{isReady=5, tyre=Tyre{id=1, grip='1'}, gear=null, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
      , Car{isReady=5, tyre=Tyre{id=1, grip='1'}, gear=Gear{id=1, type='1'}, deliveryDate=Thu Oct 10 11:27:20 IDT 2019}
      , Car{isReady=3, tyre=null, gear=null, deliveryDate=Fri Oct 11 11:27:20 IDT 2019}
      ]
      

      【讨论】:

      • @champ.exe 你怎么看?
      • @champ.exe 你可以改变 getIntegerValueForNullCheck 如果你想让非空值出现在空值之前你可以改变 getIntegerValueForNullCheck 如下:private Integer getIntegerValueForNullCheck (Object o) { return o == null ? 1 : 0; }
      • @thomas 比我的还好,不知道这个功能不错!
      • 关于样式的建议:嵌套if(value == 0) { value = someComparison(); if(value == 0 ) { ... } } 会导致代码难以阅读,尤其是在需要考虑更多条件的情况下。使用if(value == 0) { value = someComparison(); } if(value == 0) { value = someOtherComparison(); } ... 消除了这种嵌套并使代码更具可读性。
      • @Thomas 首先让我说您对这篇文章的回答以最好的方式解决了可读性问题,因此对此表示敬意。关于你对我说的话,我不是 100% 肯定我同意你的观点,因为将代码分成不同的 if 块表明每个 if 块是独立于另一个的,但在这个例子中,它们依赖于每一个,在我看来,仅当deliveryDate 相等时,保留代码以最好的方式描述想要的行为继续检查Ready 值等等......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-13
      • 2013-02-06
      • 2019-06-25
      • 2022-07-11
      相关资源
      最近更新 更多