リストから整数を適切に削除する<整数>


201

これが私が今遭遇した素晴らしい落とし穴です。整数のリストを考えてみましょう:

List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);

あなたが実行すると何が起こるかについての教育を受けた推測はありますlist.remove(1)か?どうlist.remove(new Integer(1))ですか?これはいくつかの厄介なバグを引き起こす可能性があります。

整数のリストを処理するときに、remove(int index)指定されたインデックスから要素を削除するとremove(Object o)参照によって要素を削除するを区別する適切な方法は何ですか?


ここで考慮すべき主要な点は、@ Nikitaが言及したものです -正確なパラメーターの一致は、オートボクシングよりも優先されます。


11
A:ここでの本当の問題は、Sunの誰かがプリミティブの周りに(不変の)ラッパークラスを持っていることが賢明だと考え、その後、自動(非)ボクシングがさらに賢いだ誰かが思ったということです... より良いものが存在する場合。多くの目的で、新しいArraylist <Integer>よりも優れた解決策があります。たとえば、TroveはTIntArrayListを提供します。Javaでプログラミングすればするほど(2001年からSCJP)、ラッパークラスの使用が減り、適切に設計されたAPI(Trove、Googleなどが頭に浮かびます)を使用するようになります。
SyntaxT3rr0r 2010

回答:


230

Javaは常に、引数に最も適したメソッドを呼び出します。自動ボクシングと暗黙のアップキャストは、キャスト/自動ボクシングなしで呼び出せるメソッドがない場合にのみ実行されます。

Listインターフェースは2つの削除メソッドを指定します(引数の名前に注意してください):

  • remove(Object o)
  • remove(int index)

つまりlist.remove(1)、位置1のオブジェクトが削除remove(new Integer(1))され、指定された要素の最初の出現がこのリストから削除されます。


110
nitの選択:Integer.valueOf(1)は、よりも良い方法ですnew Integer(1)。静的メソッドはキャッシングなどを実行できるため、パフォーマンスが向上します。
decitrig 2011

Peter Lawreyの提案はより優れており、不要なオブジェクトの作成を回避します。
アッシリアス2013年

@assylias:Peter Lawreyの提案はdecitrigの提案とまったく同じことを行いますが、透明性は低くなります。
Mark Peters

@MarkPeters私のコメントはについてでしたがnew Integer(1)、私はそれに同意するInteger.valueOf(1)(Integer) 1同等です。
アッシリアス2013年

68

キャストが使えます

list.remove((int) n);

そして

list.remove((Integer) n);

nがintであるかIntegerであるかは関係ありません。メソッドは常に期待するものを呼び出します。

(Integer) nまたはを使用するInteger.valueOf(n)new Integer(n)、最初の2つが整数キャッシュを使用できるよりも効率的ですが、後者は常にオブジェクトを作成します。


2
なぜそうなるのか説明していただければ幸いです:) [オートボクシングの条件...]
Yuval Adam

キャストを使用することで、コンパイラが期待する型を認識できるようになります。前者の場合、 '(int)n'はint型のみで、後者の場合、 '(Integer)n'はInteger型のみです。'n'は必要に応じて変換/ボックス化/ボックス化解除されます。変換できない場合は、コンパイラエラーが発生します。
Peter Lawrey、

10

「適切な」方法についてはわかりませんが、あなたが提案した方法はうまくいきます:

list.remove(int_parameter);

指定された位置にある要素を削除し、

list.remove(Integer_parameter);

リストから指定されたオブジェクトを削除します。

これは、VMが最初はまったく同じパラメーター型で宣言されたメソッドを見つけようと試み、その後オートボクシングを試みるためです。


7

list.remove(4)はの完全一致なlist.remove(int index)ので、呼び出されます。電話をかけたい場合list.remove(Object)は、次のようにしますlist.remove((Integer)4)


おかげで、(Integer)あなたが上に書いたような単純なキャストは、私にとって最も簡単なアプローチのようです。
vikingsteve 2013

最後のアプローチを使用すると、ブール値を返すようです。複数の削除をスタックしようとすると、ブールに対して削除を呼び出せないというエラーが発生します。
Bram Vanroy、2015年

4

list.remove(1)を実行したときに何が起こるかについて、知識のある推測はありますか?list.remove(new Integer(1))はどうですか?

推測する必要はありません。最初のケースはList.remove(int)呼び出され、positionの要素は1削除されます。2番目のケースでは、結果List.remove(Integer)として呼び出され、値が等しい要素がInteger(1)が削除されます。どちらの場合も、Javaコンパイラーは最も一致するオーバーロードを選択します。

はい、ここでは混乱(およびバグ)の可能性がありますが、それはかなりまれなユースケースです。

2つのList.removeメソッドがJava 1.2で定義されたとき、オーバーロードがあいまいではありませんでした。この問題は、Java 1.5でのジェネリックスとオートボクシングの導入によってのみ発生しました。後から見ると、removeメソッドの1つに別の名前が付けられていたほうがよかったでしょう。しかし、今では遅すぎます。


2

VMが正しいことを行わなかったとしても、remove(java.lang.Object)任意のオブジェクトを操作するという事実を使用することにより、適切な動作を保証できることに注意してください。

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}

この「解決策」は、 equals、特に(Javadocから)メソッド対称です。null以外の参照値xおよびyの場合、x.equals(y)は、y.equals( x)はtrueを返します。 " そのため、Javadocはこれが有効であると言っているListため、Listのどの実装でもxとyを自由に入れ替えることが許可されているため、のすべての実装で動作することは保証されていません。x.equals(y)Object.equals
Erwin Bolwidt 2017

1

単に私は、受け入れられた回答の最初のコメントで#decitrigによって提案されたように、次のようにしました。

list.remove(Integer.valueOf(intereger_parameter));

これは私を助けました。コメントをありがとう#decitrig。それはある人のために役立つかもしれません。


0

さてここにトリックがあります。

ここで2つの例を見てみましょう。

public class ArrayListExample {

public static void main(String[] args) {
    Collection<Integer> collection = new ArrayList<>();
    List<Integer> arrayList = new ArrayList<>();

    collection.add(1);
    collection.add(2);
    collection.add(3);
    collection.add(null);
    collection.add(4);
    collection.add(null);
    System.out.println("Collection" + collection);

    arrayList.add(1);
    arrayList.add(2);
    arrayList.add(3);
    arrayList.add(null);
    arrayList.add(4);
    arrayList.add(null);
    System.out.println("ArrayList" + arrayList);

    collection.remove(3);
    arrayList.remove(3);
    System.out.println("");
    System.out.println("After Removal of '3' :");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

    collection.remove(null);
    arrayList.remove(null);
    System.out.println("");
    System.out.println("After Removal of 'null': ");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

  }

}

次に、出力を見てみましょう。

Collection[1, 2, 3, null, 4, null]
ArrayList[1, 2, 3, null, 4, null]

After Removal of '3' :
Collection[1, 2, null, 4, null]
ArrayList[1, 2, 3, 4, null]

After Removal of 'null': 
Collection[1, 2, 4, null]
ArrayList[1, 2, 3, 4]

次に、出力を分析しましょう。

  1. コレクションから3が削除されると、パラメーターとして受け取るremove()コレクションのメソッドが呼び出さObject oれます。したがって、オブジェクトを削除します3。しかし、arrayListオブジェクトでは、インデックス3によってオーバーライドされるため、4番目の要素が削除されます。

  2. オブジェクト削除の同じロジックにより、2番目の出力ではどちらの場合もnullが削除されます。

したがって3、オブジェクトである数値を削除するには、明示的に3をとして渡す必要がありobjectます。

そして、それはラッパークラスを使用してキャストまたはラップすることによって行うことができますInteger

例えば:

Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.