使用自定义对象消除或避免在 ArrayList 中添加重复项

Jav*_*ake 2 java arraylist duplicates duplicate-removal

我在这个结构中有一个自定义对象

static class Node {
    int col;
    int row;
    int g;
    int h;
    int f;

    public Node(int col, int row, int g, int h) {
        this.col = col;
        this.row = row;
        this.g = g;
        this.h = h;
        this.f = g+h;
    }
}
Run Code Online (Sandbox Code Playgroud)

colrow变量是独一无二的,可一次只发生ArrayList<Node> myList

有没有一种最佳方法可以避免添加或检查可能的重复项而不必进行令人讨厌的 for 循环?

我知道Set接口可能是一个解决方案,因为不会发生重复,但我现在有很多代码,除非有必要,否则我不想重构。

Bri*_*ian 5

这是您的选择。所有这些解决方案都需要正确实现equalshashCode。因为你想要row并且col是独一无二的:

public boolean equals(Object obj) {
    if (obj == null || obj.getClass() != Node.class) {
        return false;
    }
    Node other = (Node) obj;
    if (other.col != this.col) {
        return false;
    }
    if (other.row != this.row) {
        return false;
    }
    return true;
}

public int hashCode() {
    int result = 7;
    result += row * 31;
    result += col * 31;
    return result;
}
Run Code Online (Sandbox Code Playgroud)

迭代 List

您不必自己进行迭代,但这正是调用List.contains将要做的。这个很简单:

if (!myList.contains(node)) {
    myList.add(node);
}
Run Code Online (Sandbox Code Playgroud)

这将为您迭代,因此您不必编写循环。

ListSetList

这里有两个子选项。如果要保留输入列表的顺序,则可以使用LinkedHashSet. 如果你不在乎,你可以使用HashSet. 我的意思是,如果我有一个List元素 A、B、C,将其转换为 aHashSet并返回可能会产生一个不同的列表,如 B、C、A。LinkedHashSet保持元素插入顺序,避免这个问题。无论如何,您只需执行以下操作:

Set<Node> nodeSet = new [Linked]HashSet<Node>(myList);
nodeSet.add(node);
myList = new ArrayList<Node>(nodeSet);
Run Code Online (Sandbox Code Playgroud)

请记住,这本质上也是在进行迭代,但它使用哈希码快捷方式而不是检查每个元素的相等性,这对于足够多的节点来说可能是一件大事。如果您的节点列表很小(少于 1000 个元素),那么我怀疑这会产生很大的不同,您不妨使用第一个。

将所有内容转换为 Set

您提到这将需要在您的代码中进行大量重构,但这并不是一件坏事,特别是如果您计划在未来大量使用此代码。我的经验法则是,如果重构会使代码更易于维护,那么增加一点额外的开发时间绝不是一件坏事。编写可维护、可读和可理解的代码是专家们所做的(这里的问题不相关,但这个特定的答案是相关的)。既然Set暗示了独特的元素而List没有,那么做出改变是有意义的。编译器几乎会告诉您必须更改其错误的所有位置,并且可能比您想象的要花费更少的时间。