我有一个对象列表,如List。 Entity类有一个equals方法,在少数属性(业务规则)上区分一个Entity对象与另一个。
我们通常在此列表中执行的任务是删除所有重复的内容:
List<Entity> noDuplicates = new ArrayList<Entity>();
for(Entity entity: lstEntities)
{
int indexOf = noDuplicates.indexOf(entity);
if(indexOf >= 0 )
{
noDuplicates.get(indexOf).merge(entity);
}
else
{
noDuplicates.add(entity);
}
}
现在,我一直在观察的问题是,一旦列表中的对象超过10000,代码的这一部分就会大大减慢。我知道arraylist正在进行o(N)搜索。
是否有更快的替代方案,使用HashMap不是一个选项,因为实体的唯一性是基于它的4个属性构建的,将密钥本身放入映射中会很繁琐吗?将更快的查询排序设置帮助?
由于
答案 0 :(得分:3)
而不是列表结构,您可以使用一个集合(如果您关注实体唯一性,则更合适),正如Lars所建议的那样。此外,如果性能有问题,我会考虑使用TreeSet并实施Comparator来根据属性比较实体实例。树结构将允许快速(对数复杂度)插入,删除和检索操作。
答案 1 :(得分:2)
一个想法是使用Set
而不是List
,Set
中没有重复项。要从列表中删除重复项,您只需将List
添加到新的Set
List<Entity> list = //your list.
Set<Entity> set = new HashSet<Entitiy>();
set.addAll(list);
但话说回来,也许有一些理由首先使用List
?如果没有,您可以使用Set
代替,而不必担心任何重复。
修改
Set
中的元素没有索引引用(与List
相比,您可以get(int index)
执行此操作。 Set
中的元素在没有特定参考点的情况下浮动。
如果您需要找到特定的一个,您需要遍历它们。如果这不合适和/或您不能没有索引引用 - 允许get(int index)
和remove(int index)
- 我猜Set
不适合您。
答案 2 :(得分:2)
现在,我一直在观察的问题是,只要列表的对象超过10000,代码的这一部分就会大大减慢。我理解arraylist正在进行o(N)搜索。
您发布的算法实际上比O(N)
更差lstEntities
- O(N)ArrayList.indexOf(T)
- 再次O(N)您的算法实际上是O(N ^ 2),因为您可能在循环内扫描列表两次。
听起来你想要做的事实上是两个操作:
List
中删除所有重复项您可以通过仅扫描一次列表而不是嵌套循环来执行此操作。我建议您拆分Entity
以将“标识”实体的字段移动到其他类型,例如ID
,或者至少添加一个getID()
方法,该方法可以返回这些字段分组为单一类型。这样,您可以轻松地在两种类型之间构建Map,以便能够合并具有“重复”标识的实体。这可能看起来像这样:
Map<ID, Entity> map = new HashMap<ID, Entity>(inputList.size());
for (Entity e : inputList) {
Entity existing = map.get(e.getID());
if (existing == null) {
//not in map, add it
map.put(e.getID(), e);
}
else {
existing.merge(e);
}
}
遍历列表是O(n),而HashMap.get(K)
是一个恒定时间操作。
答案 3 :(得分:1)
这一切都取决于merge
操作正在做什么。执行merge
时,equals
是否更改了所比较的任何属性?如果没有,那么你会惊讶于如果这样做会更快:
首先,为hashCode
类定义Entity
,与equals
的定义兼容。一种常见的方法是:
public int hashCode() {
// assuming the four attributes that determine equality are called
// attrFoo, attrBar, attrBaz, and attrQux
int hash = 1;
hash += attrFoo == null ? 0 : attrFoo.hashCode();
hash *= 37;
hash += attrBar == null ? 0 : attrBar.hashCode();
hash *= 37;
hash += attrBaz == null ? 0 : attrBaz.hashCode();
hash *= 37;
hash += attrQux == null ? 0 : attrQux.hashCode();
return hash;
}
然后,使用HashMap
以便您可以找到以下内容:
Map<Entity, Entity> map = new HashMap<Entity, Entity>();
for(Entity entity: lstEntities) {
if (map.containsKey(entity)) {
map.get(entity).merge(entity);
} else {
map.put(entity, entity);
}
}
return map.values(); // or keys(). Whichever.
我应该注意到,编写上面的代码时我感觉有点脏,因为你真的不应该创建不是一成不变的Map
个键,但是这样做会比你的更快,更快。现在就做。
答案 4 :(得分:0)
除非你有理由需要订购List,否则你可能最好使用Set - 特别是HashSet。
我看到你对使用散列集合的担忧,因为“实体的唯一性是建立在它的4个属性上”,但这很容易克服。你只需要定义一个与你现有的equals()方法兼容的hashcode()方法,然后你可以将你的实体插入一个Set中,作为一个神奇的副作用,永远不必再删除重复。
答案 5 :(得分:0)
O(N * Log(N))算法的两个简单步骤: