有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

集合在java中是否可以制作类似Comparator的东西,但要实现自定义equals()和hashCode()

我有一个对象数组,我想将它与另一个对象数组连接起来,除了具有相同id的对象。这些对象在系统中的许多地方都使用,并且没有实现哈希代码或相等值。所以我不想实现hashCode()equals(),因为我害怕破坏系统中使用这些对象的某个地方,我对此一无所知

我想把所有这些对象放在一个集合中,但不知何故,让这些对象使用自定义hashCode()equals()。类似于custom Comparator,但适用于equals


共 (3) 个答案

  1. # 1 楼答案

    当然,您可以创建一些提供等式比较和哈希代码的外部对象。但是Java的内置集合不使用这样的对象进行比较/查找

    我曾经在我的软件包集合中创建过这样的界面(刚刚翻译成英文):

    public interface HashableEquivalenceRelation {
    
        /**
         * Returns true if two objects are considered equal.
         *
         * This should form an equivalence relation, meaning it
         * should fulfill these properties:
         *  <ul>
         *    <li>Reflexivity:  {@code areEqual(o, o)}
         *            should always return true.</li>
         *    <li>Symmetry: {@code areEqual(o1,o2) == areEqual(o2,o1)}
         *            for all objects o1 and o2</li>
         *    <li>Transitivity: If {@code areEqual(o1, o2)} and {@code areEqual(o2,o3)},
         *            then {@code areEqual(o1,o3}} should hold too.</li>
         *  </ul>
         * Additionally, the relation should be temporary consistent, i.e. the
         * result of this method for the same two objects should not change as
         * long as the objects do not change significantly (the precise meaning of
         * <em>change significantly</em> is dependent on the implementation).
         *
         * Also, if {@code areEqual(o1, o2)} holds true, then {@code hashCode(o1) == hashCode(o2)}
         * must be true too.
         */
        public boolean areEqual(Object o1, Object o2);
    
        /**
         * Returns a hashCode for an arbitrary object.
         *
         * This should be temporary consistent, i.e. the result for the same
         * objects should not change as long as the object does not change significantly
         * (with change significantly having the same meaning as for {@link areEqual}).
         *
         * Also, if {@code areEqual(o1, o2)} holds true, then {@code hashCode(o1) == hashCode(o2)}
         * must be true too.
         */
        public int hashCode(Object o);
    
    }
    

    然后我有一组接口CustomCollectionCustomSetCustomListCustomMap等等。这些接口的定义与java.util中的接口类似,但对所有方法都使用这种等价关系,而不是Object.equals给出的内置关系。我也有一些默认实现:

    /**
     * The equivalence relation induced by Object#equals.
     */
    public final static EquivalenceRelation DEFAULT =
        new EquivalenceRelation() {
            public boolean areEqual(Object o1, Object o2)
            {
                return
                    o1 == o2 ||
                    o1 != null &&
                    o1.equals(o2);
            }
            public int hashCode(Object ob)
            {
                return
                    ob == null?
                    0 :
                    ob.hashCode();
            }
            public String toString() { return "<DEFAULT>"; }
        };
    
    /**
     * The equivalence relation induced by {@code ==}.
     * (The hashCode used is {@link System#identityHashCode}.)
     */
    public final static EquivalenceRelation IDENTITY =
        new EquivalenceRelation() {
            public boolean areEqual(Object o1, Object o2) { return o1 == o2; }
            public int hashCode(Object ob) { return System.identityHashCode(ob); }
            public String toString() { return "<IDENTITY>"; }
        };
    
    /**
     * The all-relation: every object is equivalent to every other one.
     */
    public final static EquivalenceRelation ALL =
        new EquivalenceRelation() {
            public boolean areEqual(Object o1, Object o2) { return true; }
            public int hashCode(Object ob) { return 0; }
            public String toString() { return "<ALL>"; }
        };
    
    /**
     * An equivalence relation partitioning the references
     * in two groups: the null reference and any other reference.
     */
    public final static EquivalenceRelation NULL_OR_NOT_NULL =
        new EquivalenceRelation() {
            public boolean areEqual(Object o1, Object o2)
            {
                return (o1 == null && o2 == null) ||
                    (o1 != null && o2 != null);
            }
            public int hashCode(Object o) { return o == null ? 0 : 1; }
            public String toString() { return "<NULL_OR_NOT_NULL>"; }
        };
    
    /**
     * Two objects are equivalent if they are of the same (actual) class.
     */
    public final static EquivalenceRelation SAME_CLASS =
        new EquivalenceRelation() {
            public boolean areEqual(Object o1, Object o2)
            {
                return o1 == o2 || o1 != null && o2 != null &&
                    o1.getClass() == o2.getClass();
            }
            public int hashCode(Object o) { return o == null ? 0 : o.getClass().hashCode(); }
            public String toString() { return "<SAME_CLASS>"; }
        };
    
    
    /**
     * Compares strings ignoring case.
     * Other objects give a {@link ClassCastException}.
     */
    public final static EquivalenceRelation STRINGS_IGNORE_CASE =
        new EquivalenceRelation() {
            public boolean areEqual(Object o1, Object o2)
            {
                return o1 == null ?
                    o2 == null :
                    ((String)o1).equalsIgnoreCase((String)o2);
            }
            public int hashCode(Object o)
            {
                return o == null ? -12345 : ((String)o).toUpperCase().hashCode();
            }
            public String toString() { return "<STRINGS_IGNORE_CASE>"; }
        };
    
    
    /**
     * Compares {@link CharSequence} implementations by content.
     * Other object give a {@link ClassCastException}.
     */
    public final static EquivalenceRelation CHAR_SEQUENCE_CONTENT =
        new EquivalenceRelation() {
            public boolean areEqual(Object o1, Object o2) 
            {
                CharSequence seq1 = (CharSequence)o1;
                CharSequence seq2 = (CharSequence)o2;
                if (seq1 == null ^ seq2 == null) // nur eins von beiden null
                    return false;
                if (seq1 == seq2)   // umfasst auch den Fall null == null
                    return true;
                int size = seq1.length();
                if (seq2.length() != size)
                    return false;
                for (int i = 0; i < size; i++)
                    {
                        if (seq1.charAt(i) != seq2.charAt(i))
                            return false;
                    }
                return true;
            }
            /**
             * Entrspricht String.hashCode
             */
            public int hashCode(Object o)
            {
                CharSequence sequence = (CharSequence)o;
                if (sequence == null)
                    return 0;
                int hash = 0;
                int size = sequence.length();
                for (int i = 0; i < size; i++)
                    {
                        hash = hash * 31 + sequence.charAt(i);
                    }
                return hash;
            }
        };
    
  2. # 2 楼答案

    90%的时候,当用户想要一个等价关系时,已经有了一个更直接的解决方案。你想只基于ID消除一堆东西的重复吗?你能把它们都放到一个以ID为键的映射中,然后得到它的values()集合吗

  3. # 3 楼答案

    HashingStrategy是您正在寻找的概念。它是一个策略接口,允许您定义equals和hashcode的自定义实现

    public interface HashingStrategy<E>
    {
        int computeHashCode(E object);
        boolean equals(E object1, E object2);
    }
    

    正如其他人所指出的,不能将HashingStrategy与内置的HashSetHashMap一起使用Eclipse Collections包括一个名为UnifiedSetWithHashingStrategy的集合和一个名为UnifiedMapWithHashingStrategy的映射

    让我们看一个例子。这里有一个简单的Data类,我们可以在UnifiedSetWithHashingStrategy中使用

    public class Data
    {
        private final int id;
    
        public Data(int id)
        {
            this.id = id;
        }
    
        public int getId()
        {
            return id;
        }
    
        // No equals or hashcode
    }
    

    下面是你如何设置一个UnifiedSetWithHashingStrategy并使用它

    java.util.Set<Data> set =
      new UnifiedSetWithHashingStrategy<>(HashingStrategies.fromFunction(Data::getId));
    Assert.assertTrue(set.add(new Data(1)));
    
    // contains returns true even without hashcode and equals
    Assert.assertTrue(set.contains(new Data(1)));
    
    // Second call to add() doesn't do anything and returns false
    Assert.assertFalse(set.add(new Data(1)));
    

    为什么不直接使用MapUnifiedSetWithHashingStrategy使用了UnifiedMap的一半内存,以及HashMap的四分之一内存。有时你没有一个方便的键,必须创建一个合成键,比如元组。这会浪费更多的内存

    我们如何进行查找?记住,集合有contains(),但没有get()UnifiedSetWithHashingStrategy除了MutableSet之外,还实现了Pool,因此它还实现了get()的一种形式

    注意:我是Eclipse集合的提交者