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

Java 使用自定义对象消除或避免在ArrayList中添加重复项,java,arraylist,duplicates,duplicate-removal,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 =

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

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;
    }
}
变量是唯一的,只能在
ArrayList myList
中出现一次

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


我知道
Set
接口可能是解决这一问题的一种方法,因为不会出现重复,但我现在有很多代码,除非必要,否则我不想重构这些代码。

将所有元素添加到一个新的
集合
,然后将
集合
中的所有元素放入一个新的
列表
。这就足够了。

我总是觉得奇怪,当人们需要唯一性时,我想人们想要使用一个(对于
get(int)
方法),这只能通过
Set
实现

无论如何,通过稍微操作equals/hashcode(当
相同时,使equals返回
)方法并向添加调用,您可以在不牺牲
列表
的情况下实现目标

编辑


请注意,您还可以创建一个比较器,并依靠它对列表进行排序,并将具有相同值的项仅合并为一个值。

如果可能,请在节点中添加一个equals方法:

@Override
public boolean equals(Node n){
if(this.getRow().equals(n.getRow()) && this.getCol().equals(n.getCol())
return true;
else
return false;
}
然后使用
list设置为list
trick

尝试以下方法:

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

理想情况下,您可以使用
Set
,但如果您希望避免将数据结构从
ArrayList
重新实现为
Set
,您可以将
Set
实现为网关守护者:

  • 每次将元素插入ArrayList时,请检查行-列对是否已在集合中
  • 如果不是,则将行-列对注册到集合中
  • 如果该对已存在于集合中,请不要插入它
  • 每次将要从ArrayList中删除元素时,请将其从集合中删除
因此它是一个“看门人”


所有的Set操作都是
O(1)
,因为它们是散列的;最少的重构,没有令人讨厌的循环。

保留一个集合和一个列表。使用集合检查重复项。如果没有重复,则添加到集合和列表

…假设节点已定义.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);
}
private final Set seen=new HashMap();
private final List uniqueNodes=new ArrayList();
公共void insertIfUnique(最终节点n){
如果(见包含(n)){
返回;
}
见。添加(n);
增加(n);
}

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

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.contains
所要做的。这个很简单:

if (!myList.contains(node)) {
    myList.add(node);
}
这将为您进行迭代,因此您不必编写循环

列表
设置
列表
这里有两个子选项。如果要保留输入列表的顺序,则可以使用
LinkedHashSet
。如果您不在乎,您可以使用
HashSet
。我的意思是,如果我有一个包含元素a、B、C的
列表
,将其转换为
哈希集
,然后返回可能会产生一个不同的列表,如B、C、a。
LinkedHashSet
将元素保持插入顺序,从而避免了这个问题。在任何情况下,您都可以这样做:

Set<Node> nodeSet = new [Linked]HashSet<Node>(myList);
nodeSet.add(node);
myList = new ArrayList<Node>(nodeSet);
Set nodeSet=new[Linked]HashSet(myList);
添加(node);
myList=新阵列列表(节点集);
请记住,这实际上也是在进行迭代,但它使用的是哈希代码快捷方式,而不是检查每个元素的相等性,这对于足够多的节点来说可能是一件大事。如果您的节点列表很小(少于1000个元素),那么我怀疑这会造成很大的差异,您也可以使用第一个

将所有内容转换为
Set

您提到这需要在代码中进行大量重构,但这并不是一件坏事,特别是如果您计划在将来大量使用此代码。我的经验是,如果重构能使代码更易于维护,那么增加一点额外的开发时间从来都不是一件坏事。编写可维护的、可读的和可理解的代码(这里的问题与此无关,但这个特定的答案与此相关)。由于
Set
意味着唯一的元素,而
List
则没有,因此进行更改是有意义的。编译器几乎会告诉您所有需要更改的位置及其错误,而且所花的时间可能比您想象的要少。

不要忘记重写equal()和hashCode()方法,以检查您想要实现的特定相等条件。我已经有了一个排序算法,但我正在排序一个完全不同的条件。所以,也许我可以更好地使用SET。如果我实现了SET,那么我如何根据<代码>行< /C++ >和java >代码>而不是整个元素?您可以通过重写
节点的
.equals()
来过滤重复项:如果您检查行和列是否相等,那么节点本身就相等。但是对于
数组列表
,通过重写equals方法以同样的方式不可能做到这一点?换句话说就是这样。为了让它更干净,我应该重构到
Set
并覆盖
equals()
方法?我很想遍历整个列表,尽管它会变成
O(N)
。这也是一个可行的选择。如果你的列表很小(就像我在回答中说的,<1000个元素),那么做所有这些事情都不会对速度产生太大影响。您可以随时使用
contains
编写它,看看它是否真的那么慢。如果是
Set<Node> nodeSet = new [Linked]HashSet<Node>(myList);
nodeSet.add(node);
myList = new ArrayList<Node>(nodeSet);