从 Java 中的列表中删除重复对象
Remove duplicates Objects from List in Java
我知道这种问题以前在 Whosebug 中问过很多次。但是我的问题有点不同,我找不到任何类似的情况,所以在这里发布这个问题
问题:
我需要从 ArrayList 中删除重复的对象。我的 arrayList 的结构如下
dataList.add(new ObjectClass("a","b"));
dataList.add(new ObjectClass("c","n"));
dataList.add(new ObjectClass("b","a")); // should be counted as duplicate
dataList.add(new ObjectClass("z","x"));
我需要从上面的列表中删除对象,例如,它将 "a,b" 和 "b,a" 的组合视为重复项并删除任何重复项
我的解决方案:
步骤 1) 覆盖 DataClass class
中的 equals 方法
class DataClass {
String source;
String destination;
DataClass(String src, String dest) {
this.source = src;
this.destination = dest;
}
// getter setter for source and destination variables
@Override
public boolean equals(Object obj) {
System.out.println("inside equals");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ObjectClass other = (ObjectClass) obj;
if(i.equals(other.getJ())
&& j.equals(other.getI())) {
return true;
} else return false;
}
步骤 2) 删除重复项的方法
public List<DataClass> removeDuplicates(List<DataClass> dataList) {
List<DataClass> resultList = new ArrayList<DataClass>();
// Convert array list to Linked list
LinkedList<DataClass> linkedList = new LinkedList<DataClass>();
for(DataClass obj: dataList) {
linkedList.add(obj);
}
// Iterate through linked list and remove if values are duplicates
for(int i = 0; i<linkedList.size();i++) {
for(int j = i+1;j<linkedList.size();j++) {
if(linkedList.get(j).equals(linkedList.get(i))) {
linkedList.remove();
}
}
}
resultList.addAll(linkedList);
return resultList;
}
我还在寻找更好的优化方案,如果有的话。提前致谢
更新解决方案
:需要我的 equals 方法来纠正一些比较逻辑。所以这是我更新的 ObjectClass 而不是 DataClass,包括正确的重写 equals 方法
public class ObjectClass {
String i;
String j;
public ObjectClass(String i, String j) {
this.i = i;
this.j = j;
}
// getters setters
// override hashcode
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ObjectClass other = (ObjectClass) obj;
if((i.equals(other.getJ()) || i.equals(other.getI()))
&& (j.equals(other.getI()) || j.equals(other.getJ()))) {
return true;
} else return false;
}
}
2) 修复 equals 方法后,我尝试按照 Janos 提到的 removeDuplicate 方法进行以下实现,它按预期工作正常
for(ObjectClass obj: dataList) {
if(!resultList.contains(obj))
resultList.add(obj);
}
非常感谢
示例:
DataClass dc = new DataClass("a","b");
List<DataClass> resultList = new ArrayList<DataClass>();
resultList .add(dc);
for (int i=0; i < resultList.size(); i++) {
if(resultList.get(i).source.equals(dc.source) && resultList.get(i).destination.equals(dc.destination) || resultList.get(i).source.equals(dc.destination) && resultList.get(i).destination.equals(dc.source)) {
resultList.remove(i);
}
}
对equals
和hashCode
使用HashSet
,这样不区分顺序。:
class DataClass {
String source;
String destination;
private final Set<String> content = new HashSet< String >();
DataClass(String src, String dest) {
this.source = src;
this.destination = dest;
content.add(src);
content.add(dest);
}
// getter setter for source and destination variables
@Override
public boolean equals(Object obj) {
System.out.println("inside equals");
if (this == obj)
return true;
if (obj == null)
return false;
if (!DataClass.class.equals(obj.getClass()))
return false;
DataClass other = (DataClass) obj;
return content.equals(other.content);
}
public int hashCode() {
return content.hashCode();
}
要进一步阅读如何实施 equals
和 hashCode
,您可能需要阅读 https://www.mkyong.com/java/java-how-to-overrides-equals-and-hashcode/
使用 LinkedHashSet
查找重复项:
public List<DataClass> removeDuplicates(List<DataClass> dataList) {
return new ArrayList<DataClass>(new LinkedHashSet<DataClass>(dataList));
}
为什么我们使用 LinkedHashSet
而不是 HashSet
? LinkedHashSet
保留排序,而 HashSet
不保留。引自 LinkedHashSet
javadoc:
This implementation differs from HashSet in that it maintains a
doubly-linked list running through all of its entries. This linked
list defines the iteration ordering, which is the order in which
elements were inserted into the set (insertion-order).
这里有几个问题:
class DataClass {
String source;
String destination;
// ...
@Override
public boolean equals(Object obj) {
// ...
ObjectClass other = (ObjectClass) obj;
if(i.equals(other.getJ())
&& j.equals(other.getI())) {
return true;
} else return false;
}
equals
方法将另一个对象转换为 ObjectClass
。
它应该转换为定义此方法的相同 class:DataClass
.
equals
方法比较i
和j
变量,
但它们没有在 class 中的任何地方定义。
有 source
和 destination
。
当 this.i
与 other.j
相同且 this.j
与 other.i
相同时,equals
方法将 return 为真,否则 return false。换句话说,(a, b)
将等于 (b, a)
。但它不会等于自己。这很奇怪,可能不是您想要的。
removeDuplicates
方法过于复杂。
例如,将数组列表转换为链表是不必要的。
这是一个更简单的算法:
- 对于源列表中的每个值
- 如果结果列表中不存在该值,则将其添加到结果列表中
就是这样。
List<DataClass> result = new ArrayList<>();
for (DataClass item : dataList) {
if (!result.contains(item)) {
result.add(item);
}
}
return result;
这假定 equals
方法的实现是固定的。
否则 result.contains
步骤将无法正常工作。
另请注意,result.contains
执行线性搜索:
它会检查每个项目,直到找到匹配项。
您可以通过使用集合来提高性能。
用你的代码
linkedList.remove();
每次从linkedList
中删除一个项目时,所有后续项目的索引都会递减。那会弄乱你的迭代循环。
我知道这种问题以前在 Whosebug 中问过很多次。但是我的问题有点不同,我找不到任何类似的情况,所以在这里发布这个问题
问题: 我需要从 ArrayList 中删除重复的对象。我的 arrayList 的结构如下
dataList.add(new ObjectClass("a","b"));
dataList.add(new ObjectClass("c","n"));
dataList.add(new ObjectClass("b","a")); // should be counted as duplicate
dataList.add(new ObjectClass("z","x"));
我需要从上面的列表中删除对象,例如,它将 "a,b" 和 "b,a" 的组合视为重复项并删除任何重复项
我的解决方案: 步骤 1) 覆盖 DataClass class
中的 equals 方法class DataClass {
String source;
String destination;
DataClass(String src, String dest) {
this.source = src;
this.destination = dest;
}
// getter setter for source and destination variables
@Override
public boolean equals(Object obj) {
System.out.println("inside equals");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ObjectClass other = (ObjectClass) obj;
if(i.equals(other.getJ())
&& j.equals(other.getI())) {
return true;
} else return false;
}
步骤 2) 删除重复项的方法
public List<DataClass> removeDuplicates(List<DataClass> dataList) {
List<DataClass> resultList = new ArrayList<DataClass>();
// Convert array list to Linked list
LinkedList<DataClass> linkedList = new LinkedList<DataClass>();
for(DataClass obj: dataList) {
linkedList.add(obj);
}
// Iterate through linked list and remove if values are duplicates
for(int i = 0; i<linkedList.size();i++) {
for(int j = i+1;j<linkedList.size();j++) {
if(linkedList.get(j).equals(linkedList.get(i))) {
linkedList.remove();
}
}
}
resultList.addAll(linkedList);
return resultList;
}
我还在寻找更好的优化方案,如果有的话。提前致谢
更新解决方案 :需要我的 equals 方法来纠正一些比较逻辑。所以这是我更新的 ObjectClass 而不是 DataClass,包括正确的重写 equals 方法
public class ObjectClass {
String i;
String j;
public ObjectClass(String i, String j) {
this.i = i;
this.j = j;
}
// getters setters
// override hashcode
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ObjectClass other = (ObjectClass) obj;
if((i.equals(other.getJ()) || i.equals(other.getI()))
&& (j.equals(other.getI()) || j.equals(other.getJ()))) {
return true;
} else return false;
}
}
2) 修复 equals 方法后,我尝试按照 Janos 提到的 removeDuplicate 方法进行以下实现,它按预期工作正常
for(ObjectClass obj: dataList) {
if(!resultList.contains(obj))
resultList.add(obj);
}
非常感谢
示例:
DataClass dc = new DataClass("a","b");
List<DataClass> resultList = new ArrayList<DataClass>();
resultList .add(dc);
for (int i=0; i < resultList.size(); i++) {
if(resultList.get(i).source.equals(dc.source) && resultList.get(i).destination.equals(dc.destination) || resultList.get(i).source.equals(dc.destination) && resultList.get(i).destination.equals(dc.source)) {
resultList.remove(i);
}
}
对equals
和hashCode
使用HashSet
,这样不区分顺序。:
class DataClass {
String source;
String destination;
private final Set<String> content = new HashSet< String >();
DataClass(String src, String dest) {
this.source = src;
this.destination = dest;
content.add(src);
content.add(dest);
}
// getter setter for source and destination variables
@Override
public boolean equals(Object obj) {
System.out.println("inside equals");
if (this == obj)
return true;
if (obj == null)
return false;
if (!DataClass.class.equals(obj.getClass()))
return false;
DataClass other = (DataClass) obj;
return content.equals(other.content);
}
public int hashCode() {
return content.hashCode();
}
要进一步阅读如何实施 equals
和 hashCode
,您可能需要阅读 https://www.mkyong.com/java/java-how-to-overrides-equals-and-hashcode/
使用 LinkedHashSet
查找重复项:
public List<DataClass> removeDuplicates(List<DataClass> dataList) {
return new ArrayList<DataClass>(new LinkedHashSet<DataClass>(dataList));
}
为什么我们使用 LinkedHashSet
而不是 HashSet
? LinkedHashSet
保留排序,而 HashSet
不保留。引自 LinkedHashSet
javadoc:
This implementation differs from HashSet in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is the order in which elements were inserted into the set (insertion-order).
这里有几个问题:
class DataClass { String source; String destination; // ... @Override public boolean equals(Object obj) { // ... ObjectClass other = (ObjectClass) obj; if(i.equals(other.getJ()) && j.equals(other.getI())) { return true; } else return false; }
equals
方法将另一个对象转换为 ObjectClass
。
它应该转换为定义此方法的相同 class:DataClass
.
equals
方法比较i
和j
变量,
但它们没有在 class 中的任何地方定义。
有 source
和 destination
。
当 this.i
与 other.j
相同且 this.j
与 other.i
相同时,equals
方法将 return 为真,否则 return false。换句话说,(a, b)
将等于 (b, a)
。但它不会等于自己。这很奇怪,可能不是您想要的。
removeDuplicates
方法过于复杂。
例如,将数组列表转换为链表是不必要的。
这是一个更简单的算法:
- 对于源列表中的每个值
- 如果结果列表中不存在该值,则将其添加到结果列表中
就是这样。
List<DataClass> result = new ArrayList<>();
for (DataClass item : dataList) {
if (!result.contains(item)) {
result.add(item);
}
}
return result;
这假定 equals
方法的实现是固定的。
否则 result.contains
步骤将无法正常工作。
另请注意,result.contains
执行线性搜索:
它会检查每个项目,直到找到匹配项。
您可以通过使用集合来提高性能。
用你的代码
linkedList.remove();
每次从linkedList
中删除一个项目时,所有后续项目的索引都会递减。那会弄乱你的迭代循环。