我正在寻找将给定集合划分为不相交子集的代码。例如,一组足球运动员,我们根据他们所属的球队对他们进行分区。我最终想要一份代表名单,即每个队员中的一名队员。
所有足球运动员都了解球队中的所有其他球员 - 这与复杂性非常相关。所以,我目前关于如何做到这一点的想法如下(其中set
目前是LinkedHashSet<T>
):
while (!set.isEmpty()) {
E e = set.iterator().next();
makeRepresentative(e);
set.remove(AllPlayersOnSameTeamAs(e));
}
然而,在while循环的每个步骤中构建一个新的迭代器感觉很奇怪。 LinkedHashSet应该在内部具有某种firstElement()
函数(对于其LinkedList行为),但由于某种原因我无法找到如何执行此操作。我也试过了一个foreach循环,但结果是java.util.ConcurrentModificationException
。
我该如何正确地做到这一点?
答案 0 :(得分:1)
while (!set.isEmpty()) {
Collection<E> toBeRemoved = new ArrayList<E>();
E first = set.iterator().next();
doSomethingWith(e);
for (E e : set) {
if (similar(first, e)) toBeRemoved.add(e);
}
set.removeAll(toBeRemoved);
}
在更好地阅读您的编辑和理解之后,这里有一个您可能喜欢的解决方案:
Collection<E> processed = new ArrayList<E>();
for (E e1 : set) {
boolean similar = false;
for (E e2 : processed) {
if (similar(e1, e2)) similar = true;
}
if (!similar) {
doSomethingWith(e1);
processed.add(e1);
}
}
set.clear();
请注意,在不了解“类似”的定义的情况下,这个问题本质上是二次的。它可以被制成线性或次二次的唯一方法是,如果有一种方法将相似的元素散列到同一个键。在这种情况下,您可以使用上面的第二个策略,但修改processed
结构和检查以前类似元素的部分更有效(目前该步骤在类似组的数量上是线性的,可能是总元素中的线性)。
此外,任何次级二次方肯定会使用的不仅仅是常量内存。如果你想要恒定的记忆,这是你能做的最好的事情(绝对是二次时间):
while (!set.isEmpty()) {
Iterator<E> iter = set.iterator();
E first = iter.next();
doSomethingWith(first);
while (iter.hasNext()) {
if (similar(first, iter.next())) iter.remove();
}
}
请注意,使用iter.remove()可以修复以前的并发修改问题。
答案 1 :(得分:0)
我会一次性完成,跟踪我见过的球队。
Set<Team> processedTeams = new HashSet<>();
Set<Players> representatives = new HashSet<>();
for(e:players) {
Team t = e.getTeam();
if(processedTeams.contains(t))
continue;
processedTeams.add(t);
representatives.add(e)
}