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

时间:2012-11-06 14:45:46

标签: 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;
    }
}

colrow变量是唯一的,并且只能在ArrayList<Node> myList中出现一次。

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

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

6 个答案:

答案 0 :(得分:4)

以下是您的选择。所有这些解决方案都需要正确实施equalshashCode。由于您希望rowcol是唯一的:

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;
}

迭代List

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

if (!myList.contains(node)) {
    myList.add(node);
}

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

ListSetList

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

Set<Node> nodeSet = new [Linked]HashSet<Node>(myList);
nodeSet.add(node);
myList = new ArrayList<Node>(nodeSet);

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

将所有内容转换为Set

你提到过这需要在你的代码中进行大量的重构,但这并不是件坏事,特别是如果你计划将来对这段代码进行大量的处理。我的经验法则是,如果重构会使代码更容易维护,添加一点额外的开发时间永远是一件坏事。编写可维护,可读且易于理解的代码is what the experts do(这里的问题不相关,但这个特定的答案是)。由于Set表示唯一元素而List没有,因此进行更改是有意义的。编译器几乎会告诉你所有你必须改变它的错误的地方,它可能比你想象的花费更少的时间。

答案 1 :(得分:1)

如果可能,在Node中添加equals方法:

@Override
public boolean equals(Node n){
if(this.getRow().equals(n.getRow()) && this.getCol().equals(n.getCol())
return true;
else
return false;
}

然后使用list-to-set-to-list技巧。

试试这个方法:

List<Node> removeDuplicateNodes(List<Node> inputList){
return (inputList ==null or inputList.size()==0)? Collections.EMPTY_LIST: new ArrayList<Node>(new HashSet<Node>(inputList));
}

更新:

如果您不想维护输入订单,请使用LinkedHashSet

List<Node> removeDuplicateNodes(List<Node> inputList){
return (inputList ==null or inputList.size()==0)? Collections.EMPTY_LIST: new ArrayList<Node>(new LinkedHashSet<Node>(inputList));
}

答案 2 :(得分:0)

将所有元素添加到新Set,然后将Set中的所有元素添加到新List。这将成为现实。

答案 3 :(得分:0)

我总是觉得很奇怪看到人们想要使用List(我认为get(int)方法)需要单一性的情况,这只能通过Set实现。

无论如何,通过操纵一点equals / hashcode(当truerow相同时)等于返回col方法并将调用添加到List#contains(Object),你可以在不牺牲List

的情况下达到目标

修改

请注意,您还可以创建一个比较器并依靠Collections#sort(List, Comparator)对列表进行排序,并将具有相同值的项目合并为一个值。

答案 4 :(得分:0)

理想情况下,您使用的是Set,但如果您希望避免重新实现从ArrayList<Node>Set的数据结构,则可以将Set实现为看门人:

  • 每次要将元素插入到ArrayList中时,请检查row-col对是否已在Set中。
  • 如果没有,请将row-col对注册到Set
  • 如果该对中已存在该对,请勿插入
  • 每次要从ArrayList中删除元素时,请将其从Set中删除。

因此它是一个“看门人”。

所有Set操作都是O(1),因为它们是经过哈希处理的;最小的重构和没有讨厌的循环。

答案 5 :(得分:0)

同时保留Set和List。使用Set检查重复项。如果没有欺骗,则添加到Set和List。

...假设Node定义了一个.equals方法......

private final Set<Node> seen = new HashMap<Node>();
private final List<Node> uniqueNodes = new ArrayList<Node>();


public void insertIfUnique(final Node n) {
  if (seen.contains(n)) {
    return;
  }
  seen.add(n);
  uniqueNodes.add(n);
}