【问题标题】:Check if a Particular Object is in a List Using a Given Implementation of equals()使用给定的 equals() 实现检查特定对象是否在列表中
【发布时间】:2013-12-03 11:26:20
【问题描述】:

我需要检查特定对象的等效实例是否在列表中。

对象是具有过于严格的 equals 方法的最终类的实例。我希望能够为“包含”方法提供equals 的不同实现,以检查对象是否包含在列表中。

如果partsInBox的元素顺序不同,下面类中的equals方法会返回false;我需要将这种行为更改为不分青红皂白的顺序。

public final class Box {
    String category;
    List<Integer> partsInBox;

    @Override
    public boolean equals(Object o) {
        if (this == o) { return true; }
        if (o == null || getClass() != o.getClass()) { return false; }

        Box box = (Box) o;

        return category.equals(box.category) 
                && partsInBox.equals(box.partsInBox);
    }
}

我希望能够做这样的事情:

List<Box> boxes; // list that I am checking
Box myBox; // what I am checking for

boolean contained = contatins(boxes, box, new EqualsMethod() {
    @Override
    public boolean areEqual(Box b1, Box b2) {
        if (b1 == b2) { return true; }

        return b1.category.equals(b2.category)
                && b1.partsInBox.containsAll(b2.partsInBox);
    }
});

实现此类功能有哪些选择?

【问题讨论】:

  • @tintinmj 一点也不。代码审查站点用于寻求对有效代码的改进。在这里,迈克提出了一个关于如何做某事的问题。他需要... stackoverflow.com。
  • 这样的equals() 实现(使用containsAll)会违反正确定义的equals() 方法的对称 属性。
  • 正确的是 areEqual() 是邪恶的。
  • @GriffeyDog Good Catch,可能需要b1.containsAll(b2) &amp;&amp; b2.containsAll(b1)

标签: java list collections contains


【解决方案1】:

您无法提供其他方法来与列表进行比较。最好和最简单的解决方案是修改您的 equals() 方法。如果你不能修改 equals,你可以实现一个 Decorator 类来创建一个带有你需要的 areEquals 比较的列表。

public class BoxList<E extends Box> implements List<E>{
    private List<E> list;

    public BoxList(List<E> list) {
        this.list = list;
    }

    //Modify the behavior of the methods
    @Override
    public boolean contains(Object o) {
        for(E element : list) {
            if (element.areEquals(o)) {
                return true;
            }
        }
        return false;
    }

    // Redirect all other List methods to the original list
    @Override
    public boolean add(E e) {
        return list.add(e);
    }
    @Override
    public void add(int index, E element) {
        list.add(index, element);        
    }
    ...

【讨论】:

    【解决方案2】:

    在 Groovy 中使用闭包会更容易(但不是那么低效)。好吧,我们开始使用 Java:

    package test;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    // ------- Original code with comments added
    
    public final class Box {
    
        // these should be final and List<Integer> should be immutable using
        // Collections.unmodifiableList() to avoid nasty surpises, and should 
        // possibly be pre-sorted
    
        String category;
        List<Integer> partsInBox;
    
        @Override
        public boolean equals(Object o) {
            // same instance, then true (works if null passed, too)
            if (this == o) {
                return true;
            }
            // Null and not exactly same class (instanceof not needed as "final"), then false
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Box box = (Box) o;
            // otherwise same category and exactly same list (including ordering)
            return category.equals(box.category) && partsInBox.equals(box.partsInBox);
        }
    }
    
    // ------- Create a wrapper class around Box, repainting the house
    
    class WrappedBox {
    
        final Box box;     
    
        WrappedBox(Box box) {
            assert box != null;
            this.box = box;
        }
    
        public String getCategory() {
            return box.category;        
        }
    
        public List<Integer> getPartsInBox() {
            return box.partsInBox;        
        }
    
        public boolean equals(Object o) {
            // same instance, then true (works if null passed, too)
            if (this == o) {
                return true;
            }
            // Null and not same class, then false
            if (o == null || !(o instanceof WrappedBox)) {
                return false;
            }
            //
            // otherwise same category and the set of b1 parts is a superset of the set of b2 parts
            // this is not symmetric; should probably be a set comparison. What happens if there
            // are several integers with the same value??
            // return b1.category.equals(b2.category)
            //        && b1.partsInBox.containsAll(b2.partsInBox);
            //
            // SO RECODE AS AUXILIARY EXERCISE:
            //
            WrappedBox other = (WrappedBox)o;
            if (!this.getCategory().equals(other.getCategory())) {
                return false;
            }
            //
            // You probably want to buffer these somehow:
            //
            List<Integer> x1 = new ArrayList(this.getPartsInBox());
            List<Integer> x2 = new ArrayList(other.getPartsInBox());
            Collections.sort(x1);
            Collections.sort(x2);
            return x1.equals(x2); 
        }
    }
    
    // --------- Now we can ask for "contains", though one should really create a 
    // ---------- List<WrappedBox> first if this happens often
    
    class BoxHandler {
    
        static boolean containsBox(List<Box> boxes, Box box) {
            assert box != null;
            assert boxes != null;
            WrappedBox wbox = new WrappedBox(box);
            for (Box cur : boxes) {
                if (wbox.equals(new WrappedBox(cur))) {
                    return true;
                }
            }
            return false;
        }
    }
    

    【讨论】:

      【解决方案3】:

      理想的解决方案是改变 equals() 方法的当前行为。但是,如果您无权访问其他代码,则可能无法实现。

      相反,您可以使用 Apache CollectionUtils 中的 CollectionUtils.exists(collection, predicate)

      http://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/CollectionUtils.html

      您可以使用您需要的自定义条件创建一个谓词,以确定您的对象是否足够

      希望对你有帮助。

      【讨论】:

        【解决方案4】:

        Equator.java

        public interface Equator<T> {
            boolean equals(T obj1, T obj2);
        }
        

        其他类

        public static <T> boolean contains(Collection<T> toSearch, T toSeek, Equator<T> equator) {
            for (T oneItem : toSearch) {
                if (equator.equals(oneItem, toSeek)) {
                    return true;
                }
            }
            return false;
        }
        

        使用它

        import static some.other.class.contains; // The contains method from the class above
        
        List<Box> boxes; // list that I am checking
        Box myBox; // what I am checking for
        
        boolean contained = contains(boxes, box, new Equator<Box>() {
            @Override
            public boolean equals(Box b1, Box b2) {
                if (b1 == b2) { return true; }
        
                return b1.category.equals(b2.category)
                        && b1.partsInBox.containsAll(b2.partsInBox);
            }
        });
        

        【讨论】:

          【解决方案5】:

          您可以使用带有 Java 内置方法的 Comparator 进行排序和二分搜索。假设您有一个这样的类,其中 a 和 b 是您要用于排序的字段:

          class Thing { String a, b, c, d; }
          

          您将定义您的比较器:

          Comparator<Thing> comparator = new Comparator<Thing>() {
            public int compare(Thing o1, Thing o2) {
              if (o1.a.equals(o2.a)) {
                return o1.b.compareTo(o2.b);
              }
              return o1.a.compareTo(o2.a);
            }
          };
          

          然后对您的列表进行排序:

          Collections.sort(list, 比较器); 最后进行二分查找:

          int i = Collections.binarySearch(list, thingToFind, comparator);
          

          【讨论】:

            【解决方案6】:

            既然类是final,你不能扩展它。

            但是,您可以使用 Comparator&lt;T&gt; 接口,如下所示:

            public class BoxComparator implements Comparator<Box> {
                @Override
                public int compare(Box b1, Box b2) {
                    if (b1 == b2) { return 0; }
            
                    // return -1 or 0 or +1...
                }
            
                public static void main(String[] args) {
                    Box box1, box2;
                    ...
            
                    boolean contains = new BoxComparator().compare(box1, box2) == 0;
                }
            }
            

            如果您想将 Box 与另一个 BoxList&lt;Box&gt; 进行比较,我不能完全确定从上面的代码示例中 - 在后一种情况下,您无法派生 Comparator,但您可以做类似的事情,例如BoxInListComparator

            希望这会有所帮助。

            【讨论】:

            • 我想知道一个盒子列表中是否有一个特定盒子的等效实例。
            猜你喜欢
            • 1970-01-01
            • 2015-11-03
            • 1970-01-01
            • 2019-07-04
            • 1970-01-01
            • 2018-03-20
            • 1970-01-01
            • 1970-01-01
            • 2016-07-26
            相关资源
            最近更新 更多