Java:Equalator? (从对象集合中删除重复项)

时间:2010-01-01 04:45:48

标签: java collections similarity comparator equivalence

我有一堆类Puzzle的对象。我已覆盖equals()hashCode()。当需要向用户提供解决方案时,我想过滤掉所有“相似”的拼图(按照我定义的标准),因此用户只能看到其中一个。

相似性是可传递的。

示例:

Result of computations:
A    (similar to A)
B    (similar to C)
C
D

在这种情况下,只会向用户呈现A或D和B或C,但不会出现两个类似的谜题。两个相似的谜题同样有效。重要的是它们不会向用户显示。

为实现这一目标,我想使用禁止重复的ADT。但是,我不想更改equals()hashCode()方法来返回关于相似性的值。在这种情况下,我可以使用Equalator之类的Comparator吗?或者我应该采取另一种方式吗?

我正在上课的是一个维护字母网格的拼图。 (与拼字游戏一样。)如果拼图包含相同的单词,但其方向不同,则认为它是相似的。以下是谜题:

                                    (2, 2): A           
                                    (2, 1): C           
                                    (2, 0): T

类似于:

                    (1, 2): A           
                    (1, 1): C           
                    (1, 0): T      

5 个答案:

答案 0 :(得分:2)

我使用的包装类会相应地覆盖equalshashCode

private static class Wrapper {
    public static final Puzzle puzzle;
    public Wrapper(Puzzle puzzle) { 
        this.puzzle = puzzle; 
    }
    @Override 
    public boolean equals(Object object) {
        // ...
    }
    @Override 
    public int hashCode() {
        // ...
    }
}

然后你把所有的谜题都包起来,把它们放在地图上,再把它们拿出来......

public Collection<Collection<Puzzle>> method(Collection<Puzzles> puzzles) {
    Map<Wrapper,<Collection<Puzzle>> map = new HashMap<Wrapper,<Collection<Puzzle>>();
    for (Puzzle each: puzzles) {
        Wrapper wrapper = new Wrapper(each);
        Collection<Puzzle> coll = map.get(wrapper);
        if (coll == null) map.put(wrapper, coll = new ArrayList<Puzzle>());
        coll.add(puzzle);
    }
    return map.values();
}

答案 1 :(得分:2)

好的,你有办法测量物体之间的相似性。这意味着它们形成Metric Space

问题是,你的空间是Euclidean space还是正常的三维空间,还是整数或类似的东西?如果是,那么您可以使用binary space partition来获得多个维度。

(问题是,基本上:你的物体和n维实数矢量之间是否存在同态?如果是,那么你可以使用技术来测量n维空间中点的紧密度。)

现在,如果不是欧洲空间那么你就会遇到更大的问题。程序员可能最熟悉的非欧几里德空间的一个例子是字符串之间的Levenshtein Distance

如果您的问题类似于查看字符串与已存在字符串列表的相似程度,那么我不会知道任何没有O(n 2)的算法)时间。也许那里有一些。


但另一个重要问题是:你有多少时间?有多少个物体?如果您有时间或者您的数据集足够小以至于O(n 2 )算法是可行的,那么您只需迭代对象列表以查看它是否低于某个阈值。如果是这样,请拒绝它。

只需重载AbstractCollection并替换Add函数。使用ArrayList或其他。你的代码看起来有点像这样

class SimilarityRejector<T> extends AbstractCollection<T>{
     ArrayList<T> base;
     double threshold;

    public SimilarityRejector(double threshold){
        base = new ArrayList<T>();
        this.threshold = threshold;
    }

    public void add(T t){
       boolean failed = false;
       for(T compare : base){
          if(similarityComparison(t,compare) < threshold) faled = true;
       }
       if(!failed) base.add(t);
     }

    public Iterator<T> iterator() {
        return base.iterator();
    }

    public int size() {
        return base.size();
    }
}

等。显然,T需要是某个类的子类,您可以对其进行比较。如果您有欧几里德指标,那么您可以使用空间分区,而不是遍历其他所有项目。

答案 2 :(得分:2)

  1. 使用比较器
  2. 创建TreeSet
  3. 将所有元素添加到集合
  4. 所有重复项都被删除

答案 3 :(得分:0)

通常情况下,“相似性”不是传递关系。因此,第一步是从等效性而非相似性的角度来考虑这一点。等价是反身的,对称的和传递的。

这里的简单方法是定义一个拼图包装器,其equals()和hashCode()方法是根据所讨论的等价关系实现的。

完成后,将包装好的对象放入java.util.Set并过滤掉重复的内容。

答案 4 :(得分:0)

恕我直言,Gili(带有自定义比较器的TreeSet)描述了最优雅的方式。

但如果您想亲自制作,那么这似乎是最简单,最清晰的解决方案:

/**
 * Distinct input list values (cuts duplications)
 * @param items items to process
 * @param comparator comparator to recognize equal items
 * @return new collection with unique values
 */
public static <T> Collection<T> distinctItems(List<T> items, Comparator<T> comparator) {
    List<T> result = new ArrayList<>();

    for (int i = 0; i < items.size(); i++) {
        T item = items.get(i);

        boolean exists = false;
        for (int j = 0; j < result.size(); j++) {
            if (comparator.compare(result.get(j), item) == 0) {
                exists = true;
                break;
            }
        }

        if (!exists) {
            result.add(item);
        }
    }

    return result;
}