web-dev-qa-db-ja.com

Javaの配列からオブジェクトを削除するにはどうすればよいですか?

nオブジェクトの配列が与えられた場合、それが文字列の配列であり、次の値を持つとしましょう:

foo[0] = "a";
foo[1] = "cc";
foo[2] = "a";
foo[3] = "dd";

配列内の"a"に等しいすべての文字列/オブジェクトを削除/削除するにはどうすればよいですか?

75
ramayac

[すぐに使用できるコードが必要な場合は、[編集3](カット後)までスクロールしてください。残りは後世のためにここにあります。]

肉体化するには ダストマンのアイデア

List<String> list = new ArrayList<String>(Arrays.asList(array));
list.removeAll(Arrays.asList("a"));
array = list.toArray(array);

編集:Arrays.asListの代わりにCollections.singletonを使用しています。シングルトンは1つのエントリに制限されていますが、asListアプローチでは他の文字列を追加して後で除外できます:Arrays.asList("a", "b", "c")

Edit2:上記のアプローチは同じ配列を保持します(したがって、配列は同じ長さです)。最後の要素はnullに設定されます。必要に応じて正確なサイズのnew配列が必要な場合は、代わりにこれを使用します。

array = list.toArray(new String[0]);

Edit3:同じクラスでこのコードを頻繁に使用する場合、クラスにこれを追加することを検討してください。

private static final String[] EMPTY_STRING_ARRAY = new String[0];

次に、関数は次のようになります。

List<String> list = new ArrayList<>();
Collections.addAll(list, array);
list.removeAll(Arrays.asList("a"));
array = list.toArray(EMPTY_STRING_ARRAY);

これにより、関数が呼び出されるたびにnewedになるような無駄な空の文字列配列でヒープが散らばることがなくなります。

cynicalmanの提案(コメントを参照)もヒープのポイ捨てを助けますが、公平を期すために言及する必要があります。

array = list.toArray(new String[list.size()]);

明示的なサイズを間違って取得する方が簡単な場合があるため、私のアプローチを好みます(たとえば、間違ったリストでsize()を呼び出す)。

104

Java 8の代替:

String[] filteredArray = Arrays.stream(array)
    .filter(e -> !e.equals(foo)).toArray(String[]::new);
27

Arrays.asList()を使用して配列からListを作成し、適切なすべての要素でremove()を呼び出します。次に、「リスト」でtoArray()を呼び出して、再び配列に戻します。

ひどくパフォーマンスは良くありませんが、適切にカプセル化すれば、後からいつでもすぐに何かをすることができます。

20
Dustman

いつでもできます:

int i, j;
for (i = j = 0; j < foo.length; ++j)
  if (!"a".equals(foo[j])) foo[i++] = foo[j];
foo = Arrays.copyOf(foo, i);
15
anon

外部ライブラリを使用できます:

org.Apache.commons.lang.ArrayUtils.remove(Java.lang.Object[] array, int index)

Apache Commons Langプロジェクトにあります http://commons.Apache.org/lang/

6
bugs_

以下のコードを参照してください

ArrayList<String> a = new ArrayList<>(Arrays.asList(strings));
a.remove(i);
strings = new String[a.size()];
a.toArray(strings);
5
Ali

配列をListに変換したり追加の配列を作成したりせずに配列から複数の要素を削除する必要がある場合は、削除するアイテムの数に依存せずにO(n)で実行できます。

ここで、aは初期配列、int... rは削除する要素の個別の順序付けされたインデックス(位置)です。

public int removeItems(Object[] a, int... r) {
    int shift = 0;                             
    for (int i = 0; i < a.length; i++) {       
        if (shift < r.length && i == r[shift])  // i-th item needs to be removed
            shift++;                            // increment `shift`
        else 
            a[i - shift] = a[i];                // move i-th item `shift` positions left
    }
    for (int i = a.length - shift; i < a.length; i++)
        a[i] = null;                            // replace remaining items by nulls

    return a.length - shift;                    // return new "length"
}  

小規模なテスト:

String[] a = {"0", "1", "2", "3", "4"};
removeItems(a, 0, 3, 4);                     // remove 0-th, 3-rd and 4-th items
System.out.println(Arrays.asList(a));        // [1, 2, null, null, null]

タスクでは、最初に配列をスキャンして「a」の位置を収集し、removeItems()を呼び出します。

4
Alex Salauyou

そのリストを作成してから削除してからアレイに戻すことについて何かが間違っていると思います。テストはしていませんが、次の方がパフォーマンスが向上すると思います。はい、私はおそらく過度に事前最適化しています。

boolean [] deleteItem = new boolean[arr.length];
int size=0;
for(int i=0;i<arr.length;i==){
   if(arr[i].equals("a")){
      deleteItem[i]=true;
   }
   else{
      deleteItem[i]=false;
      size++;
   }
}
String[] newArr=new String[size];
int index=0;
for(int i=0;i<arr.length;i++){
   if(!deleteItem[i]){
      newArr[index++]=arr[i];
   }
}
3
shsteimer

これは非常に古い投稿であることがわかりますが、ここでの回答のいくつかは私を助けてくれたので、ここに私の大金の価値があります!

ArrayListに加えられた変更がリストのサイズを変更しない限り、私が書き戻している配列のサイズを変更する必要があることをいじる前に、かなり前からこれを機能させるのに苦労しました。

変更しているArrayListが開始時よりも多いまたは少ない要素で終わる場合、List.toArray()行で例外が発生するため、List.toArray(new String[] {})またはList.toArray(new String[0])新しい(正しい)サイズの配列を作成します。

私はそれを知っているので、今では明らかです。新しい/馴染みのないコード構成に慣れているAndroid/Java初心者にはそれほど明らかではなく、ここにある以前の投稿のいくつかからも明らかではないので、私がそうであったように何時間も頭をかく他の人にはこの点を本当に明確にしたかったです!

2
DDSports

ここにはたくさんの答えがあります-私が見る問題は、コレクションの代わりに配列を使用している理由を言わなかったということですので、いくつかの理由とどのソリューションが適用されるかを提案させてください(ほとんどのソリューションここで他の質問で既に回答されているので、私はあまり詳細には行きません):

理由:コレクションパッケージが存在することを知らなかったか、または信頼していませんでした

解決策:コレクションを使用します。

途中から追加/削除する予定がある場合は、LinkedListを使用します。サイズが本当に心配な場合、またはコレクションの真ん中に頻繁にインデックスを付ける場合は、ArrayListを使用します。これらの両方に削除操作が必要です。

理由:サイズが気になるか、メモリ割り当てを制御したい

解決策:特定の初期サイズでArrayListを使用します。

ArrayListは、それ自体を拡張できる単なる配列ですが、常に拡張する必要はありません。アイテムの追加/削除は非常に賢明ですが、ここでもLOTを中央から挿入/削除する場合は、LinkedListを使用します。

理由:入ってくる配列と出て行く配列がある-だから、配列を操作したい

解決策:ArrayListに変換し、アイテムを削除してから元に戻す

理由:自分でやればより良いコードを書くことができると思う

解決策:配列リストまたはリンクリストを使用することはできません。

理由:これはクラス割り当てであり、許可されていないか、何らかの理由でコレクションAPIにアクセスできません

仮定:新しい配列を正しい「サイズ」にする必要があります

解決策:一致するアイテムの配列をスキャンし、それらをカウントします。正しいサイズの新しい配列を作成します(元のサイズ-一致数)。 System.arraycopyを繰り返し使用して、保持するアイテムの各グループを新しい配列にコピーします。これがクラスの割り当てであり、System.arraycopyを使用できない場合は、ループで手動で1つずつコピーしますが、実稼働コードではこれを実行しないでください。 (これらのソリューションは両方とも他の回答で詳しく説明されています)

理由:ベアメタルを実行する必要があります

仮定:不必要にスペースを割り当てたり、時間をかけすぎてはいけません

仮定:配列で使用されるサイズ(長さ)を個別に追跡します。そうしないと、削除/挿入のために配列を再割り当てする必要があります。

これを行う理由の例:プリミティブの単一の配列(たとえばint値)がRAMのかなりの部分を占めている(50%など)! ArrayListは、これらを強制的に整数オブジェクトへのポインターのリストにし、その数倍のメモリを使用します。

解決策:配列を反復処理し、削除する要素が見つかったら(要素nと呼びましょう)、System.arraycopyを使用して、配列の末尾を「deleted」要素(コピー元とコピー先は同じ配列)にコピーします。メモリがそれ自体を上書きしないように、正しい方向でコピーを行うのに十分スマートです:

 System.arraycopy(ary、n + 1、ary、n、length-n)
 length-; 

一度に複数の要素を削除する場合は、おそらくこれよりも賢くなりたいでしょう。テール全体ではなく、ある「マッチ」と次の「マッチ」の間でのみエリアを移動し、いつものように、チャンクを2回移動することは避けます。

この最後のケースでは、絶対に自分で作業を行う必要があります。System.arraycopyを使用するのは、コンピューターアーキテクチャのメモリを移動するための最適な方法を選択するためです。自分で合理的に記述できるコードよりも。

2
Bill K

編集:

配列内のヌルのあるポイントはクリアされました。コメントありがとうございます。

元の:

ええと...ライン

array = list.toArray(array);

削除された要素があった配列内のすべてのギャップをnullで置き換えます。これはdangerousである可能性があります。これは、要素は削除されますが、配列の長さは変わらないためです!

これを回避する場合は、toArray()のパラメーターとして新しい配列を使用します。 removeAllを使用したくない場合は、Setが代わりになります。

        String[] array = new String[] { "a", "bc" ,"dc" ,"a", "ef" };

        System.out.println(Arrays.toString(array));

        Set<String> asSet = new HashSet<String>(Arrays.asList(array));
        asSet.remove("a");
        array = asSet.toArray(new String[] {});

        System.out.println(Arrays.toString(array));

与える:

[a, bc, dc, a, ef]
[dc, ef, bc]

Chris Yester Youngからの現在受け入れられている答えは次のとおりです。

[a, bc, dc, a, ef]
[bc, dc, ef, null, ef]

コード付き

    String[] array = new String[] { "a", "bc" ,"dc" ,"a", "ef" };

    System.out.println(Arrays.toString(array));

    List<String> list = new ArrayList<String>(Arrays.asList(array));
    list.removeAll(Arrays.asList("a"));
    array = list.toArray(array);        

    System.out.println(Arrays.toString(array));

null値が残されていません。

1
GHad

初期配列

   int[] array = {5,6,51,4,3,2};

インデックス2である51を削除する場合は、次を使用します

 for(int i = 2; i < array.length -1; i++){
    array[i] = array[i + 1];
  }
1
Ebin Joy

この問題への私の小さな貢献。

public class DeleteElementFromArray {
public static String foo[] = {"a","cc","a","dd"};
public static String search = "a";


public static void main(String[] args) {
    long stop = 0;
    long time = 0;
    long start = 0;
    System.out.println("Searched value in Array is: "+search);
    System.out.println("foo length before is: "+foo.length);
    for(int i=0;i<foo.length;i++){ System.out.println("foo["+i+"] = "+foo[i]);}
    System.out.println("==============================================================");
    start = System.nanoTime();
    foo = removeElementfromArray(search, foo);
    stop = System.nanoTime();
    time = stop - start;
    System.out.println("Equal search took in nano seconds = "+time);
    System.out.println("==========================================================");
    for(int i=0;i<foo.length;i++){ System.out.println("foo["+i+"] = "+foo[i]);}
}
public static String[] removeElementfromArray( String toSearchfor, String arr[] ){
     int i = 0;
     int t = 0;
     String tmp1[] = new String[arr.length];     
         for(;i<arr.length;i++){
              if(arr[i] == toSearchfor){     
              i++;
              }
             tmp1[t] = arr[i];
             t++;
     }   
     String tmp2[] = new String[arr.length-t];   
     System.arraycopy(tmp1, 0, tmp2, 0, tmp2.length);
     arr = tmp2; tmp1 = null; tmp2 = null;
    return arr;
}

}

1
Andre

ああ、コードを正しく表示できません。申し訳ありませんが、動作しました。申し訳ありませんが、質問を適切に読んだとは思いません。

String  foo[] = {"a","cc","a","dd"},
remove = "a";
boolean gaps[] = new boolean[foo.length];
int newlength = 0;

for (int c = 0; c<foo.length; c++)
{
    if (foo[c].equals(remove))
    {
        gaps[c] = true;
        newlength++;
    }
    else 
        gaps[c] = false;

    System.out.println(foo[c]);
}

String newString[] = new String[newlength];

System.out.println("");

for (int c1=0, c2=0; c1<foo.length; c1++)
{
    if (!gaps[c1])
    {
        newString[c2] = foo[c1];
        System.out.println(newString[c2]);
        c2++;
    }
}
0
AngelOfCake

要素の順序が重要でない場合。要素foo [x]とfoo [0]を交換してから、foo.drop(1)を呼び出すことができます。

foo.drop(n)は、配列から(n)個の最初の要素を削除します。

これが最も簡単でリソース効率の良い方法だと思います。

PSindexOfはさまざまな方法で実装できます。これは私のバージョンです。

Integer indexOf(String[] arr, String value){
    for(Integer i = 0 ; i < arr.length; i++ )
        if(arr[i] == value)
            return i;         // return the index of the element
    return -1                 // otherwise -1
}

while (true) {
   Integer i;
   i = indexOf(foo,"a")
   if (i == -1) break;
   foo[i] = foo[0];           // preserve foo[0]
   foo.drop(1);
}
0
milevyo

インデックスiの要素を除くすべての要素をコピーします。

if(i == 0){
                System.arraycopy(edges, 1, copyEdge, 0, edges.length -1 );
            }else{
                System.arraycopy(edges, 0, copyEdge, 0, i );
                System.arraycopy(edges, i+1, copyEdge, i, edges.length - (i+1) );
            }
0
PauLy

のような文字列の配列で

String name = 'a b c d e a f b d e' // String name = 'aa bb c d e aa f bb d e'

次のクラスを作成します

class clearname{
def parts
def tv
public def str = ''
String name
clearname(String name){
    this.name = name
    this.parts = this.name.split(" ")
    this.tv = this.parts.size()
}
public String cleared(){

        int i
        int k
        int j=0        
    for(i=0;i<tv;i++){
        for(k=0;k<tv;k++){
            if(this.parts[k] == this.parts[i] && k!=i){
               this.parts[k] = '';
                j++
            }
        }
    }
    def str = ''
    for(i=0;i<tv;i++){
        if(this.parts[i]!='')

           this.str += this.parts[i].trim()+' '
    } 
    return this.str    
}}



return new clearname(name).cleared()

この結果を得る

a b c d e f

このコードが誰にでも役立つことを願っています

0
Orlando Reyes

「削除」の意味に依存しますか?配列は固定サイズのコンストラクトです-配列内の要素の数は変更できません。したがって、a)不要な要素のない新しい短い配列を作成するか、b)不要なエントリを「空」ステータスを示すものに割り当てることができます。通常、プリミティブを使用していない場合はnullです。

最初のケースでは、配列からリストを作成し、要素を削除して、リストから新しい配列を作成します。パフォーマンスが重要な場合は、配列を繰り返し処理して、リストに削除すべきではない要素を割り当て、リストから新しい配列を作成します。 2番目のケースでは、単に配列項目にnullを割り当てて割り当てます。

0
DJClayworth