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


82

n個のオブジェクトの配列が与えられ、それが文字列の配列であり、次の値を持っているとしましょう。

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

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


2
Javaで配列のサイズを変更することはできません。些細なことなので、要素を単にnullにしたくないと思います。ギャップを取り除くために他の要素をシフトしますか?
ダン・ダイアー

1
それができることがわかった今、それは些細なことです。;)ありがとう!
ramayac 2008

回答:


112

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

肉付けするにDustmanのアイデアを

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に設定されます。あなたがしたい場合は、新しい配列が正確に要求されるようなサイズ、これを代わりに使用します。

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);

これによりnew、関数が呼び出されるたびに編集される、役に立たない空の文字列配列がヒープに散らばるのを防ぐことができます。

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

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

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


よろしくお願いします。最初のインスタンスだけでなく、「a」のすべてのインスタンスの削除をサポートするようにエントリを修正しました。:-)
クリスジェスター-ヤング

Ooff ...フィニッシュラインで撃墜されました。私は編集を続けるべきだと知っていました。このシステムには多少の慣れが必要です。良い編集!
ダストマン

GHad:上記の私のEdit2を読んだことがありますか?それはあなたが言ったことを正確に扱っており、あなたの投稿の前に投稿されました。
クリスジェスター-ヤング

2
新しいString [0]ではなくlist.toArray(new String [list.size()])を使用しないのはなぜですか。正しいサイズの場合、コードは新しい配列を使用するからです。
cynicalman 2008

はい、それは機能します。それ以外の場合、一部のコード(主にJavaクラスライブラリ内)は、String [0](および他の同様のゼロサイズの配列)の静的インスタンスを格納し、毎回新しいインスタンスを作成する代わりに静的インスタンスを渡します。:-)
クリスジェスター

31

Java 8の代替手段:

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

これは受け入れられた答えでなければなりません。他のプログラミング言語がより少ない明確なコードでこれを行うことができることを十分に考慮していませんが...これは、別のライブラリに依存せずにJavaが提供しなければならない最高のものです。
ジェイソン

7
きちんとしていますが、これはSystem.arraycopyと比較して驚くほど非効率的です。おそらく、実際のコードでこれを行うべきではありません。
ビルK

fooが動的文字列変数の場合、コンパイル時エラーがスローされます。囲んでいるスコープで定義されたローカル変数fooは、最終または実質的に最終である必要があります
Mahender Reddy Yasa 2018

質問には「n個のオブジェクトの配列が与えられた」と書かれています—それでStream.of(foo).filter(s -> ! s.equals("a")).toArray()十分でしょう。
カプラン

20

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

それほどパフォーマンスは良くありませんが、適切にカプセル化すれば、後でいつでもより迅速に何かを行うことができます。


3
あなたのコメントを再確認してください:それは大丈夫です、あなたはすぐにそれに慣れるでしょう。:-) Arrays.asList()(不変のリスト)の結果から要素を削除できるという考えを読者に知られたくないので、投稿を投稿しました。そのため、例でそれを処理できると思いました。:-)
クリスジェスター-ヤング

ええと、私はサイズ変更できないリストを意味しました(add()とremove()は機能しません)。:-Pまだ使用可能なset()メソッドがあります。:-)
クリスジェスター-ヤング

奇妙に思えるかもしれませんが、私の経験では、このアプローチのパフォーマンスの低下は最小限です。
マーカスダウニング

8
これは何ですか?@Chrisは、結果のリストArrays.asList()はをサポートしていないと指摘しましたremove()。それで、この答えは完全に無効ですか?一部のコメントが削除されたようですので、これが議論されたかどうかはわかりません。
LarsH 2012年

1
ChrisとLarsHはどちらも正しいです。配列に裏打ちされたリスト(つまり、Arrays.asList()で作成されるリスト)は構造的に不変であり、この答えを完全に無効にします。それでも、今のところ、17の賛成票があります。1つの不思議...します
イゴールSoudakevitch

15

あなたはいつでもすることができます:

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);


5

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

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

4

配列に変換し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()


1
これをしないでください。紛らわしく、遅く、エラーが発生しやすくなります。代わりにSystem.arraycopy()を使用してください。ただし、この方法で配列を操作する場合は、長さを追跡する必要があるという事実にボーナスポイントがあります。
ビルK

4

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

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

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

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

理由:サイズが心配であるか、メモリ割り当てを制御したい

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

ArrayListは、それ自体を展開できる単なる配列ですが、必ずしもそうする必要はありません。アイテムの追加/削除については非常に賢明ですが、LOTを途中から挿入/削除する場合は、LinkedListを使用してください。

理由:配列が入ってくる配列と出て行く配列があるので、配列を操作したい

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

理由:自分でやればもっと良いコードを書けると思う

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

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

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

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

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

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

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

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

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

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

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

この最後のケースでは、絶対に自分で作業を行う必要があります。System.arraycopyを使用することが、コンピュータアーキテクチャのメモリを移動するための最良の方法を選択するため、実際に行う唯一の方法です。これは、何倍も高速である必要があります。あなたが合理的に自分で書くことができるどんなコードよりも。


3

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

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

これは非常に古い投稿だと思いますが、ここでの回答のいくつかは私を助けてくれたので、これが私のタペンスです 'ha'ペニーの価値があります!

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

場合ArrayListあなたがアップし、それはで起動よりも大きいか少ない要素で終わるを変更していることを、ラインはList.toArray()あなたのような何かを必要とするので、例外が発生しますList.toArray(new String[] {})か、List.toArray(new String[0])新しい(正しい)サイズの配列を作成するために。

私がそれを知った今、明白に聞こえます。新しくてなじみのないコード構造に慣れてきたAndroid / Javaの初心者にはそれほど明白ではなく、ここでの以前の投稿のいくつかからは明らかではないので、私のように何時間も頭を悩ませている他の人にとってこの点を本当に明確にしたかっただけです!!


他のコーダーが当たり前と思っていることを見逃したために機能しないコードスニペットをよく使用するため、これを投稿する必要があると感じました。GHadは、私のコードを機能させる配列サイズについて指摘しました(それを明確にしてくれてありがとう)。いろいろ試してみるのが学ぶ方法です。それが、SOからコードを取得し、それがどのように/なぜ機能するのかを理解しようとすることで得られるすべての価値があることを意味するのであれば、そうです。無給のアマチュアとして、私はJavaの天才ではありません。ありがたいことに、SO寄稿者のほとんどは、他の人がより良いコードを書くのを助けるために質問に答えます。そのために、あなたは感謝に値します!
DDSports 2013

2

初期配列

   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

編集:

配列内のヌルのあるポイントがクリアされました。コメントしてすみません。

元の:

えーと...ライン

array = list.toArray(array);

削除された要素がnullであった配列内のすべてのギャップを置き換えます。要素が削除されるため、これは危険な場合がありますが、配列の長さは同じままです。

これを回避したい場合は、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値を残さずに。


良い試みですが、葉巻はありません。あなたが投稿するずっと前に、私はまさにこのトピックに関する編集を投稿しました。ですから、あなたは「技術的に正しい」のですが、人々に私の投稿を置き換えさせようとしてくれたことに感謝しません。私はあなたがそれを知っているべきだと思った。
クリスジェスター-ヤング

それは、変位後ではなく、エラーや危険なコードを回避することです。Greetz GHad
GHad

人々が私の投稿全体(両方の補遺を含む)を読んだ場合、エラーを回避できます。人々が考えずにコードをカットアンドペーストするだけなら、彼らは得るすべてのものに値します。プログラマーは頭脳を行使するので、彼らがしていることに対して報酬を受け取ります...私は願っています。[続き]
クリスジェスター-ヤング

1
[続き]ハッシュを使用することでアイテムが乱れるという事実を人々が認識していない場合、コードも「危険」です。もちろん、思考プログラマーはこれを認識していますが、人々が考えずにカットアンドペーストするために私のコードを危険だと呼ぶなら、あなたの同じことを言うのは公正です。
クリスジェスター-ヤング

確かに、あなたはハッシュについて正しいです。そして、あなたの2回目の編集で要点が明らかなので、私はこれを読み過ぎたに違いありません。前述のように、null値と重複を含む配列を避けたかっただけです。2回目の編集に基づいてコードを変更し、Edit3に配列に関するコメントを残すことができます。あなたを攻撃したくなかった
GHad 2008

1

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

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;
}

}


これは本当に良い答えですが、優れたテストコードを使用すると、実際よりもはるかに大きく見えます。全体の解決策は、単一行の配列コピーである可能性があります(コレクションの代わりに配列を使用していた金属の非常に近くで作業している場合に実行したいのと同じ配列を使用すると仮定します)。
ビルK

ありがとう、私はそれを書いたので、読者は内部で何が起こっているのかを少し見てテストすることができます、今私はそれをもう一度見ますSystem.arraycopy(tmp1、0、tmp2、0、tmp2.length); 削除して、for(i = 0; i <(arr.length --t); i ++){tmp2 [i] = tmp1 [i];のようなものに置き換えることができます。}バイトバッファを作成し、64ビットマシンで一度に8バイトをコピーして、コピーパフォーマンスを向上させることもできます
Andre

0

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

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


0

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

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

インデックス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

次のような文字列の配列

文字列名= 'abcdeafbde' //文字列名= 'aa bb cde aa f bbde'のようになります

次のクラスを作成します

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()

この結果を得る

abcdef

このコードが誰かに役立つことを願っていますよろしく


0

要素の順序は関係ありません。要素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);
}

-3

使用する:

list.removeAll(...);
//post what char you need in the ... section

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