【问题标题】:How to iterate over a SortedSet to modify items within如何遍历 SortedSet 以修改其中的项目
【发布时间】:2011-11-29 12:51:27
【问题描述】:

假设我有一个列表。在for循环中修改list的item没有问题:

for (int i = 0; i < list.size(); i++) { list.get(i).setId(i); }

但我有一个 SortedSet 而不是列表。我该怎么做呢? 谢谢

【问题讨论】:

  • 这只有在 setId() 不改变 compareTo() 使用的字段时才有效。理想情况下,compareTo 使用的所有字段都应该是final 并且是不可变的。

标签: java iterator sortedset


【解决方案1】:

首先,Set 假定它的元素是不可变的(实际上,可变元素是允许的,但它们必须遵守一个非常具体的合同,我怀疑你的班级不会这样做)。

这意味着通常您不能像处理列表那样就地修改集合元素。

Set 支持的两个基本操作是添加和删除元素。可以将修改视为删除旧元素,然后添加新元素:

  1. 您可以在迭代时使用Iterator.remove() 处理删除;
  2. 您可以将添加的内容累积在一个单独的容器中,并在最后调用Set.addAll()

【讨论】:

  • 他没有修改数据结构..他在迭代时修改了其中的元素
【解决方案2】:

您不能修改集合的键,因为它会导致集合重新排列/重新排序。因此,迭代将如何进一步运行将是未定义的行为。

您可以使用 iterator.remove() 删除元素。但是你不能添加元素,通常更好的解决方案是将它们累积在一个新的集合中并在迭代后添加它。

Set mySet = ...;
ArrayList newElems = new ArrayList();

for(final Iterator it = mySet.iterator(); it.hasNext(); )
{
  Object elem = it.next();
  if(...)
   newElems.add(...);
  else if(...)
   it.remove();
  ...
}
mySet.addAll(newElems);

【讨论】:

  • 如果id 不是比较器中使用的字段,那么这样做是安全的。
  • @kan,他没有修改数据结构..他在迭代时修改了其中的元素。
  • @Крысa 哦,对了。当然是禁止的。如果他需要更改id,他应该it.remove元素,更改id然后newElems.add
  • @Крысa:Kan 的观点是,如果类的相等性基于 ID(正在更改),那么数据结构的完整性就会受到损害。
  • 是的,我假设id 会影响集合的比较器。如果没有,那么像往常一样使用setId 完全没有问题。
【解决方案3】:

从 Java 1.6 开始,您可以使用 NavigableSet

【讨论】:

    【解决方案4】:

    您应该使用Iterator 或者更好的是增强的for 循环语法(这取决于实现Iterable 接口的类),而不管您使用的是什么Collection。这将用于遍历集合的机制抽象出来,并允许在不影响迭代例程的情况下替换新的实现。

    例如:

    Set<Foo> set = ...
    
    // Enhanced for-loop syntax
    for (Foo foo : set) {
     // ...
    } 
    
    // Iterator approach
    Iterator it = set.iterator();
    while (it.hasNext()) {
      Foo foo = it.next();
    }
    

    编辑

    Kan 提出了一个关于修改项目密钥的好观点。假设您的类的 equals()hashCode() 方法仅基于“id”属性(您正在更改),最安全的方法是在您迭代并将它们添加到“输出”Set;例如

    SortedSet<Foo> input = ...
    SortedSet<Foo> output = new TreeSet<Foo>();
    
    Iterator<Foo> it = input.iterator();
    while (it.hasNext()) {
      Foo foo = it.next();
      it.remove(); // Remove from input set before updating ID.
      foo.setId(1);
      output.add(foo); // Add to output set.
    }
    

    【讨论】:

      【解决方案5】:

      你不能那样做。但你可以尝试,也许你会成功,也许你会得到ConcurrentModificationException。记住这一点非常重要,在迭代时修改元素可能会产生意想不到的结果。相反,您应该在某个集合中收集这些元素。并在迭代后一一修改。

      【讨论】:

        【解决方案6】:

        这只有在 id 不用于等于或用于排序集的比较器时才有效:

        int counter = 0;
        for(ElementFoo e : set) {
          e.setId(counter);
          couter++;
        }
        

        【讨论】:

        • OP 没有从列表中删除项目;他们只是在每个对象上调用一个 mutator。
        • 我不需要删除该项目。我必须更新一些字段。所以解决方案是将新/更新/删除的项目存储在另一个列表中?
        猜你喜欢
        • 2011-01-04
        • 2021-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-13
        • 2021-04-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多