【问题标题】:Modifying each item of a List in java在java中修改List的每一项
【发布时间】:2013-11-28 19:24:27
【问题描述】:

我刚刚开始使用 java 中的列表。我想知道修改列表中每个元素的推荐方法是什么?

我已经能够使用以下两种方法完成它,但它们看起来都相当不雅。有没有更好的方法可以在 java 中完成这项工作?是否推荐以下任何一种方法,或者两者都在同一水平?

//Modifying with foreach
for (String each : list)
{
    list.set(list.indexOf(each), each+ " blah");
}

//Modifying with for
for (ListIterator<String> i = list.listIterator(); i.hasNext(); i.next()) 
{
    i.next();
    list.set(i.nextIndex()-1, i.previous() + " blah yadda");
}

【问题讨论】:

  • @chrylis:他们并没有在结构上修改它。我相信您通常可以在不使迭代器无效的情况下替换列表中的元素……例如,至少对于 ArrayList。当然,我可能是错的:)
  • @Jon Skeet 我认为这实际上是一个问题,当你为每个人做的时候,你不能改变你正在经历的列表的大小。
  • @Lucas:是的,通常就是这样。当然,这里不是这种情况。
  • 当然,我只是在说明一点:)
  • 上面的代码能用吗?我认为它的 ConcurrentModificationException。修改负责循环的相同列表..!!

标签: java for-loop arraylist foreach


【解决方案1】:

我是这样工作的

    String desiredInvoice="abc-123";
    long desiredAmount=1500;

    for (ListIterator<MyPurchase> it = input.getMyPurchaseList().listIterator(); it.hasNext();) {
        MyPurchase item = it.next();
        if (item.getInvoiceNo().equalsIgnoreCase(desiredInvoice)) {
            item.setPaymentAmount(desiredAmount);
            it.set(item);
            break;
        }
     }

【讨论】:

    【解决方案2】:

    第二个版本会更好。在内部它们最终是相同的,但第二个实际上允许您修改列表,而第一个会抛出 ConcurrentModificationException。

    但是你以错误的方式使用迭代器。以下是正确的做法:

    for (final ListIterator<String> i = list.listIterator(); i.hasNext();) {
      final String element = i.next();
      i.set(element + "yaddayadda");
    }
    

    迭代器是需要修改列表的,因为它是唯一知道如何正确执行此操作而又不会混淆列表元素和顺序的迭代器。

    编辑:因为我在所有 cmets 和其他答案中都看到了这一点:

    为什么不应该在循环中使用 list.get、list.set 和 list.size

    Java 集合框架中有许多集合,每个集合都针对特定需求进行了优化。许多人使用ArrayList,它在内部使用一个数组。只要元素的数量不会随时间发生太大变化,并且具有 get、set 和 size 是对这种特定类型列表的恒定时间操作的特殊好处,这很好。

    但是,还有其他列表类型,但情况并非如此。例如,如果您有一个不断增长和/或缩小的列表,最好使用 LinkedList,因为与 ArrayList 相比,add(element) 是一个恒定时间操作,但 add(index, element), get( index) 和 remove(index) 不是!

    要获取特定索引的位置,需要从第一个/最后一个遍历列表,直到找到特定元素。因此,如果您在循环中执行此操作,则等于以下伪代码:

    for (int index = 0; index < list.size(); ++index) {
      Element e = get( (for(int i = 0; i < size; ++i) { if (i == index) return element; else element = nextElement(); }) );
    }
    

    Iterator 是一种遍历列表的抽象方式,因此它可以确保以最佳方式对每个列表进行遍历。测试表明,对于 ArrayList 使用迭代器和 get(i) 之间几乎没有时间差异,但在 LinkedList 上使用了巨大的时间差异(有利于迭代器)。

    【讨论】:

    • 我不相信第一个 实际上抛出 ConcurrentModificationException - 至少在任何仅设置元素算作非结构性更改的列表中,这将成为他们中的大多数。 LinkedList 确实 有恒定时间 size() 顺便说一下(IIRC - 我现在无法轻松检查)- 但不是 get 或`set`。
    • 与相信合作不是一个好主意,试试吧。 ;) 你会注意到大多数列表确实会抛出异常。
    • 我现在不能,但会在一小时内完成。而且“大多数”也不等同于“将”:) IIRC ArrayList(这是最常用的列表,IMO)不将 set 视为结构变化。
    • "为了得到这个列表的大小,内部发生的是从第一个元素到最后一个元素遍历整个列表并计算元素。" - 不,LinkedList 不会发生这种情况。至少不在我正在查看的代码中:public int size() { return size; }。您正在查看哪个实现 可以做到这一点?
    • @TwoThe:那是使用add。此代码不是使用add,而是使用set。正如我之前所说,这不是结构性变化。当它与 OP 的代码做同样的事情时,声称代码“可能有问题”有点奇怪......
    【解决方案3】:

    编辑:如果您知道size()get(index)set(index, value) 都是您正在使用的操作的恒定时间操作(例如ArrayList),我个人会在这种情况下跳过迭代器:

    for (int i = 0; i < list.size(); i++) {
        list.set(i, list.get(i) + " blah");
    }
    

    您的第一种方法效率低下并且可能不正确(因为indexOf 可能返回错误值 - 它会返回 first 匹配项)。您的第二种方法非常令人困惑-您两次调用next() 和一次previous,这在我看来很难理解。

    当然,任何使用List.set(index, value) 的方法对于没有恒定时间索引写入访问的列表都是低效的。正如 TwoThe 所指出的,使用ListIterator.set(value) 要好得多。 TwoThe 使用ListIterator 的方法是一种更好的通用方法。

    也就是说,在许多情况下,另一种选择是更改您的设计以将一个列表投影到另一个列表 - 无论是作为视图还是实质上。如果您不更改列表,则无需担心。

    【讨论】:

    • 如果你在一个没有固定时间运算符大小和获取的列表上使用它,这将是非常低效的,比如常用的LinkedList。在所有情况下都最好使用迭代器。
    • @TwoThe:使用ListIterator.set,这是有道理的——但问题中的代码仍然是 O(n^2)。
    • 不,这将是 O(n),因为您对列表进行了一次完整的迭代,但会立即操作这些元素。即:迭代器已经知道在哪里进行更改,不需要再次查找位置。请参阅下面的答案。
    • @TwoThe:Doh,误读了一些东西...打算删除这个,因为我正在开午餐会,需要集中精力...
    • @TwoThe:回到它 - 不,原始代码仍然是 O(n^2)。外部循环是 O(n),但是当 list.set(index, value) 是 O(n) 时,在循环内部调用 list.set(index, value) 会使它成为 O(n^2),根据 LinkedList
    【解决方案4】:

    Iterator 内部有for-each 实现。所以这两种情况之间没有区别。但是如果你试图修改元素,它会throwsConcurrentModificationException

    【讨论】:

    • 实际上,这两段代码有很大的不同。这不像一个只是重写另一个以显式使用迭代器。
    猜你喜欢
    • 2013-08-19
    • 1970-01-01
    • 1970-01-01
    • 2014-01-05
    • 2019-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    相关资源
    最近更新 更多