【问题标题】:How to get one element from LinkedHashSet in Java?如何从 Java 中的 LinkedHashSet 中获取一个元素?
【发布时间】:2012-09-12 00:10:25
【问题描述】:

我正在寻找将给定集合划分为不相交子集的代码。例如,一组足球运动员,我们根据他们所属的球队对他们进行划分。最后我想要一份代表名单,即每支球队的一名球员。

所有足球运动员都认识他们球队中的所有其他球员——这与复杂性非常相关。所以,我目前关于如何做到这一点的想法如下(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

我应该如何正确地做到这一点?

【问题讨论】:

  • 你确定Set是你想要的吗?
  • 不,这正是你的做法。你的代码很好。 (具体来说,迭代器比你想象的要便宜得多,所以没什么大不了的。)
  • @LouisWasserman 我(通常)会更好地使用 HashSet 或 LinkedHashSet 吗?如果我从不超过元素 1,不确定是否值得开销?
  • 坚持LinkedHashSet 即便如此。

标签: java iterator linkedhashset


【解决方案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() 可以解决您之前遇到的并发修改问题。

【讨论】:

  • 那在一个while循环中?除非我遗漏了一些明显的东西,否则它具有二次复杂性而不是线性...:/
  • 为什么会在while循环中?实际上,现在我想起来了,我不确定我是否完全清楚原来的问题。为什么要循环直到集合为空?如果你这样做,为什么不直接做 set.clear() 呢?
  • 我编辑了我的帖子,希望现在清楚了吗?显然我忘了提到我还需要使用元素e。实际上,我的集合被划分为子集,并且相似的是在同一个子集中。所以,我想从每个子集中选择一个代表。
  • 是的,谢谢,我现在明白了。我编辑了一个更好的答案。
【解决方案2】:

我会一次性完成,跟踪我见过的团队。

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)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-03
    • 2020-07-03
    相关资源
    最近更新 更多