我在这个结构中有一个自定义对象
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;
}
}
col
和row
变量是唯一的,并且只能在ArrayList<Node> myList
中出现一次。
有没有一种方法可以避免添加或检查可能的重复,而不必进行讨厌的for循环?
我知道Set
接口可能是一个解决方案,因为重复不会发生,但我现在有很多代码,除非有必要,否则我不想重构。
答案 0 :(得分:4)
以下是您的选择。所有这些解决方案都需要正确实施equals
和hashCode
。由于您希望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;
}
List
您不必自己进行迭代,但这正是调用List.contains
所做的事情。这个很简单:
if (!myList.contains(node)) {
myList.add(node);
}
这将为您迭代,因此您不必编写循环。
List
至Set
至List
这里有两个子选项。如果要保留输入列表的顺序,则可以使用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(当true
和row
相同时)等于返回col
方法并将调用添加到List#contains(Object)
,你可以在不牺牲List
修改强>
请注意,您还可以创建一个比较器并依靠Collections#sort(List, Comparator)
对列表进行排序,并将具有相同值的项目合并为一个值。
答案 4 :(得分:0)
理想情况下,您使用的是Set
,但如果您希望避免重新实现从ArrayList<Node>
到Set
的数据结构,则可以将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);
}