在 java 列表中合并相似对象的最佳方法是什么?
What is the best way to merge similar objects in a java List?
这是我的问题(简化版):
假设我们有一个 class:
public class MyClass{
String name;
Double amount;
String otherAttribute;
}
还有一个List<MyClass> myList
假设我们有 2 个来自 myList 的元素。假设 object1 和 object2
我想做的是:
if (object1.name.equals(object2.name){
//add amount of object2 to object1
//remove object 2 from the list
}
考虑到我有一个很大的列表(可能有 100 个元素),我想找到最佳且消耗较少的方法来做我想做的事情。
你有什么建议?
编辑:
是的,100 个项目并不大,但是对于许多不同大小的列表,我会多次调用此方法(合并相似对象)。所以这就是我想找到最佳实践的方式。
我无法覆盖 MyClass 的 equals 或 hashCode 方法,很遗憾(客户要求)
我会将对象添加到 HashMap,其中 name
是键,MyClass
是要存储的值。遍历列表中的每个对象以将它们添加到地图中。如果名称不在地图中,只需添加名称、对象对。如果它已经在地图中,则将数量添加到已存储的对象中。循环完成后,从地图中提取对象。
100 个元素对于列表来说是一个很小的尺寸,考虑到您不会重复该操作数十万次。如果是这样,我会考虑创建一个数据结构,通过搜索 属性(例如 Map
)来索引列表项,或者在合适的情况下对其进行排序并使用高效的搜索算法。
一种方法(如 Bill 所建议的)是遍历 List
,将每个元素添加到 Map
,使用 name
属性 作为键。您可以利用 put
的 return 来获知先前是否已将 name
放入地图,并将先前累积的 amount
添加到当前元素中。最后,您可以使用 values()
获得没有重复的 List
。
例如:
List<MyClass> l;
Map<String, Myclass> m = new HashMap<MyClass>();
for (MyClass elem : l) {
MyClass oldElem = m.put(elem.getName(), elem);
if (oldElem != null) {
elem.setAmount(elem.getAmount() + oldElem.getAmount());
}
}
l = new ArrayList<MyClass>(m.values());
如果您需要保留列表中的顺序,请考虑使用 LinkedHashMap
。
不幸的是,这是一个复杂度为 O(n^2) 的问题。您需要将 n 个元素与 n-1 个其他元素进行比较。没有办法做到这一点,只能暴力破解它。
但是,如果您使用 HashMap,则可以在将元素添加到 Map 之前检查 map 中的元素,这是一个 O(1) 操作。它看起来像这样:
HashMap<String, MyClass> map = new HashMap<String, MyClass>();
添加元素时:
if (map.get(obj1.name) != null) {
var obj2 = map.get(obj1.name);
obj2.amount = obj2.amount + obj1.amount;
map.put(obj1.name, obj2);
}
'Large' 是相对的,100 项肯定不多,想象一下如果你必须处理 1.000.000 items/second 的流。然后你会重新定义 large :D
在您的示例中,我认为最好避免创建一组项目名称。搜索 java HashSet 需要 O(1),因此如果哈希集中存在对象名称,则在列表中更新它。一个更好的解决方案是创建一个 HashMap,您可以在其上说例如
if(mymap.contains(thename)){
mymap.put(thename, newSum);
}
这是您如何使用它的示例。这里有一个 link 让你开始:http://java67.blogspot.gr/2013/02/10-examples-of-hashmap-in-java-programming-tutorial.html
我建议优化(如果可能的话)如果存在同名元素,甚至不对列表执行 .add() 。将基于散列的集合之一与基于 MyClass.name 的适当 equals() 和 hashCode() 实现结合使用也应该会给您带来一些不错的性能。
首先,由于您不能覆盖 equals 或 hashCode,因此您需要在与 MyClass class 相同的包中具有执行此功能的函数,因为 [=11] 中未定义访问器方法=]
其次,尝试将您的项目放在 LinkedList
中,这样您就可以非常快速地从该列表中删除重复元素,而无需移动其他项目。
使用映射来跟踪给定名称对应的数量,同时迭代列表,同时删除重复元素。这样您就不必创建新列表。
List<MyClass> myClass_l;
Map<String, MyClass> nameMyClass_m = new HashMap<String, MyClass>();
for (Iterator<MyClass> iterator = myClass_l.iterator(); iterator.hasNext(){
MyClass m = iterator.next();
if (nameAmount_m.contains(m.name)){
MyClass firstClass = m.get(m.name);
firstClass.amount += m.amount;
iterator.remove();
}
else{
nameMyClass_m.put(m.name, m);
}
}
当您完成循环时,您将在原始列表中拥有所需的项目。
这是我的问题(简化版):
假设我们有一个 class:
public class MyClass{
String name;
Double amount;
String otherAttribute;
}
还有一个List<MyClass> myList
假设我们有 2 个来自 myList 的元素。假设 object1 和 object2
我想做的是:
if (object1.name.equals(object2.name){
//add amount of object2 to object1
//remove object 2 from the list
}
考虑到我有一个很大的列表(可能有 100 个元素),我想找到最佳且消耗较少的方法来做我想做的事情。
你有什么建议?
编辑:
是的,100 个项目并不大,但是对于许多不同大小的列表,我会多次调用此方法(合并相似对象)。所以这就是我想找到最佳实践的方式。
我无法覆盖 MyClass 的 equals 或 hashCode 方法,很遗憾(客户要求)
我会将对象添加到 HashMap,其中 name
是键,MyClass
是要存储的值。遍历列表中的每个对象以将它们添加到地图中。如果名称不在地图中,只需添加名称、对象对。如果它已经在地图中,则将数量添加到已存储的对象中。循环完成后,从地图中提取对象。
100 个元素对于列表来说是一个很小的尺寸,考虑到您不会重复该操作数十万次。如果是这样,我会考虑创建一个数据结构,通过搜索 属性(例如 Map
)来索引列表项,或者在合适的情况下对其进行排序并使用高效的搜索算法。
一种方法(如 Bill 所建议的)是遍历 List
,将每个元素添加到 Map
,使用 name
属性 作为键。您可以利用 put
的 return 来获知先前是否已将 name
放入地图,并将先前累积的 amount
添加到当前元素中。最后,您可以使用 values()
获得没有重复的 List
。
例如:
List<MyClass> l;
Map<String, Myclass> m = new HashMap<MyClass>();
for (MyClass elem : l) {
MyClass oldElem = m.put(elem.getName(), elem);
if (oldElem != null) {
elem.setAmount(elem.getAmount() + oldElem.getAmount());
}
}
l = new ArrayList<MyClass>(m.values());
如果您需要保留列表中的顺序,请考虑使用 LinkedHashMap
。
不幸的是,这是一个复杂度为 O(n^2) 的问题。您需要将 n 个元素与 n-1 个其他元素进行比较。没有办法做到这一点,只能暴力破解它。
但是,如果您使用 HashMap,则可以在将元素添加到 Map 之前检查 map 中的元素,这是一个 O(1) 操作。它看起来像这样:
HashMap<String, MyClass> map = new HashMap<String, MyClass>();
添加元素时:
if (map.get(obj1.name) != null) {
var obj2 = map.get(obj1.name);
obj2.amount = obj2.amount + obj1.amount;
map.put(obj1.name, obj2);
}
'Large' 是相对的,100 项肯定不多,想象一下如果你必须处理 1.000.000 items/second 的流。然后你会重新定义 large :D
在您的示例中,我认为最好避免创建一组项目名称。搜索 java HashSet 需要 O(1),因此如果哈希集中存在对象名称,则在列表中更新它。一个更好的解决方案是创建一个 HashMap,您可以在其上说例如
if(mymap.contains(thename)){
mymap.put(thename, newSum);
}
这是您如何使用它的示例。这里有一个 link 让你开始:http://java67.blogspot.gr/2013/02/10-examples-of-hashmap-in-java-programming-tutorial.html
我建议优化(如果可能的话)如果存在同名元素,甚至不对列表执行 .add() 。将基于散列的集合之一与基于 MyClass.name 的适当 equals() 和 hashCode() 实现结合使用也应该会给您带来一些不错的性能。
首先,由于您不能覆盖 equals 或 hashCode,因此您需要在与 MyClass class 相同的包中具有执行此功能的函数,因为 [=11] 中未定义访问器方法=]
其次,尝试将您的项目放在 LinkedList
中,这样您就可以非常快速地从该列表中删除重复元素,而无需移动其他项目。
使用映射来跟踪给定名称对应的数量,同时迭代列表,同时删除重复元素。这样您就不必创建新列表。
List<MyClass> myClass_l;
Map<String, MyClass> nameMyClass_m = new HashMap<String, MyClass>();
for (Iterator<MyClass> iterator = myClass_l.iterator(); iterator.hasNext(){
MyClass m = iterator.next();
if (nameAmount_m.contains(m.name)){
MyClass firstClass = m.get(m.name);
firstClass.amount += m.amount;
iterator.remove();
}
else{
nameMyClass_m.put(m.name, m);
}
}
当您完成循环时,您将在原始列表中拥有所需的项目。