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");
}
2番目のバージョンの方が良いでしょう。内部的には最終的には同じですが、2番目のリストでは実際にリストを変更できますが、最初のリストではConcurrentModificationExceptionがスローされます。
しかし、その後、イテレーターを間違った方法で使用しています。正しく行う方法は次のとおりです。
for (final ListIterator<String> i = list.listIterator(); i.hasNext();) {
final String element = i.next();
i.set(element + "yaddayadda");
}
イテレータは、リストの要素と順序を混乱させずに適切に行う方法を知っている唯一のものであるため、リストを変更する必要があるものです。
編集:すべてのコメントと他の回答でこれを見ているため:
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(); }) );
}
イテレータはリストをトラバースする抽象的な方法であるため、トラバースが各リストに対して最適な方法で行われることを保証できます。 ArrayListのイテレータとget(i)の使用にはほとんど時間差がありませんが、LinkedListには(イテレータに有利な)大きな時間差があります。
編集:Ifsize()
、get(index)
、およびset(index, value)
はすべて、使用している操作の一定時間操作であることがわかります(例: ArrayList
)の場合、この場合は個人的にイテレーターをスキップします。
_for (int i = 0; i < list.size(); i++) {
list.set(i, list.get(i) + " blah");
}
_
最初のアプローチは非効率的で、潜在的に正しくありません(indexOf
は間違った値を返す可能性があるため、first一致を返します)。 2番目のアプローチは非常に紛らわしいです。next()
を2回呼び出し、previous
を1回呼び出すと、私の見解では理解しにくくなります。
もちろん、List.set(index, value)
を使用するアプローチは、一定時間のインデックス付き書き込みアクセスを持たないリストには非効率的です。 TwoTheが指摘したように、ListIterator.set(value)
を使用する方がはるかに優れています。 TwoTheのListIterator
を使用するアプローチは、より優れた汎用アプローチです。
そうは言っても、多くの場合、別の選択肢として、あるリストを別のリストに投影するようにデザインを変更することがあります-ビューとして、または実質的に。リストを変更しないリストを変更する場合、心配する必要はありません。
私はこのように働いた
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;
}
}
内部的にfor-each
実装のIterator
にあります。したがって、これらの2つのケース間に違いはありません。ただし、要素を変更しようとすると、throws
ConcurrentModificationException
になります。