【问题标题】:how to compare two objects each containing primitive type and collection of objects in java?java - 如何比较两个对象,每个对象都包含基本类型和java中的对象集合?
【发布时间】:2019-10-11 12:33:58
【问题描述】:

我有两个 DTO 对象,如下所示,请注意我使用 lombok 来避免样板代码。

DtoA

import lombok.Data;
import java.util.List;

@Data
public class DtoA {

    private String name;
    private String number;
    private List<String> aList;
}

DtoB

import lombok.Data;
import java.util.List;

@Data
public class DtoB {
    private String name;
    private String phone;
    private List<String> bList;
}

我想比较两个对象的特定字段,所以我创建了一个适配器类型的对象,如下所示

DtoAdapter

import lombok.Data;
import java.util.List;

@Data
public class DtoAdapter {
    private String nameText;
    private List<String> xList;
}

以下是我尝试进行比较的带有 main 方法的测试类 此比较失败,因为 aList 和 bList 包含不同顺序的字符串。 我想比较列表的内容而不用担心它们的顺序。

测试

import junit.framework.Assert;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        DtoA a = new DtoA();
        List<String> aList = new ArrayList<>();
        aList.add("x"); aList.add("y"); aList.add("z");
        a.setName("abc"); a.setNumber("123"); a.setAList(aList);

        DtoB b = new DtoB();
        List<String> bList = new ArrayList<>();
        bList.add("z"); bList.add("x"); bList.add("y");
        b.setName("abc"); b.setPhone("123"); b.setBList(bList);

        DtoAdapter a1 = new DtoAdapter();
        a1.setNameText(a.getName()); a1.setXList(a.getAList());

        DtoAdapter b1 = new DtoAdapter();
        b1.setNameText(b.getName()); b1.setXList(b.getBList());

        // comparision failing because of lists contains string in different orders
        Assert.assertEquals(a1, b1);
    }
}

注意: 我试过写 compareTo (通过在 DtoAdapter 类中实现可比较的接口) 但是我无法使用如下的 compareTo 方法来比较两个列表

具有类似接口的DtoAdapter

import lombok.Data;
import java.util.List;

@Data
public class DtoAdapter implements Comparable<DtoAdapter>{
    private String nameText;
    private List<String> xList;

    @Override
    public int compareTo(DtoAdapter o) {
        return this.getNameText().compareTo(o.getNameText());
        // how to compare this.getXList() and o.getXList() with compareTo?
    }
}

【问题讨论】:

    标签: java java-8


    【解决方案1】:

    您期待的不是扩展Comparable,而是为该类扩展覆盖的equals 实现(当然还有hashcode)。

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DtoAdapter that = (DtoAdapter) o;
        // following line of code specifically
        return Objects.equals(nameText, that.nameText) &&
                that.getXList().containsAll(xList) && xList.containsAll(that.getXList());
    }
    

    进一步从Comparable 的文档,该接口主要用于排序元素而不是相等比较:

    此接口对每个类的对象进行总排序 实现它。这种排序被称为类的 自然排序,类的compareTo方法被称为 它的自然比较法

    【讨论】:

    • 加一;但有两件事:1)如果你实现Comparable,强烈建议你重写equals/hashCode 2)我希望有时会有一个接口EqualsAndHashCode......我有时只想在一个方法中使用这些类型,并且我希望我能说List&lt;? extends EqualsAndHashCode&gt;....
    【解决方案2】:

    就像@Naman 的回答一样,您可以覆盖方法 equals 但尝试首先比较列表的长度,因为如果长度相同,您只需要检查第一个列表的元素是否在另一个列表中。

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } 
    
        if (!(o instanceof DtoAdapter)) {
            return false;
        }
    
        DtoAdapter that = (DtoAdapter) o;
    
        return Objects.equals(nameText, that.nameText) && that.getXList().size() == xList.size() && that.getXList().containsAll(xList);
    
    }
    

    【讨论】:

      【解决方案3】:

      我想比较列表的内容而不用担心 他们的顺序。

      我已经实现了一个实用方法,它允许compare List ignoring the order

      该库可从 Maven Central 获得:

      <dependency>
        <groupId>org.softsmithy.lib</groupId>
        <artifactId>softsmithy-lib-core</artifactId>
        <version>2.0</version>
      </dependency>
      

      源代码可从GitHub获取。

      还要注意实现 Comparable 的最佳实践:https://stackoverflow.com/a/46287029/506855

      【讨论】:

        【解决方案4】:

        您可以在类 DtoAdapter 中 @Override 方法 equals。

        @Override
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            } 
        
            if (!(o instanceof DtoAdapter)) {
                return false;
            }
        
            DtoAdapter c = (DtoAdapter) o;
        
            if(c.getXList().size() != xList.size()) {
                return false;
            }
        
            // write logic for object comparison
        
            return c.getNameText().equals(nameText);
        }
        

        如果您不想关心列表的排序方式,可以使用方法 removeAll()。

        首先,检查列表的大小是否相同。然后,您使用另一个列表作为参数从一个列表中删除所有元素。

        c.getXList().removeAll(xList);
        

        删除所有元素后,检查第一个列表是否还有任何元素。如果不是,则表示列表中包含相同的元素。

        【讨论】:

        • 您不应该从任一集合中删除元素。您只需要检查两个列表是否包含相同的元素,无论顺序如何。为什么要拆除? equals 这样的查询方法不应该有副作用
        【解决方案5】:

        您可以考虑使用 String 类的 compareTo(),如下代码所示。

        @Override
        public int compareTo(DtoAdapter o) {
            int cmpName = this.getNameText().compareTo(o.getNameText());
            // how to compare this.getXList() and o.getXList() with compareTo?
        
            int cmpList = 0;
            if(this.list.size() != o.list.size()) {
                cmpList = this.list.size() - o.list.size();
            } else {
                for(int i = 0; i < list.size(); i++) {
                    int v = list.get(i).compareTo(o.list.get(i));
                    if(v != 0) {
                        cmpList = v;
                        break;
                    }
                }
            }
        
            return cmpName + cmpList;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-12
          • 2012-04-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-10
          相关资源
          最近更新 更多