Як видалити об'єкти з масиву в Java?


82

Враховуючи масив з n Об’єктів, припустимо, що це масив рядків , і він має такі значення:

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

Що мені потрібно зробити, щоб видалити / видалити всі рядки / об'єкти, рівні "a" у масиві?


2
Ви не можете змінити розмір масиву в Java. Я припускаю, що ви не хочете просто обнуляти елементи, оскільки це було б тривіально. Ви хочете зрушити інші елементи, щоб усунути прогалини?
Dan Dyer

1
Це тривіально, тепер, коли я знаю, що можу це зробити. ;) Дякую!
ramayac

Відповіді:


112

[Якщо вам потрібен готовий до використання код, прокрутіть до мого "Редагувати3" (після вирізу). Решта тут для нащадків.]

Щоб конкретизувати ідею Дастмена :

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

Змінити: Я зараз , використовуючи Arrays.asListзамість Collections.singleton: Сінглтон обмежується однією записи, в той час як 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редагуватися кожного разу, коли викликається ваша функція.

Пропозиція цинічного чоловіка (див. коментарі) також допоможе у смітті купи, і заради справедливості я повинен це згадати:

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

Я віддаю перевагу своєму підходу, тому що може бути простіше неправильно визначити розмір (наприклад, виклик size()неправильного списку).


Радий, що сподобався. Я переглянув свій запис, щоб підтримати видалення всіх випадків "а", а не лише першого. :-)
Кріс Джестер-Янг

Офф ... збитий на фініші. Знав, що я мав продовжувати редагувати. Ця система вимагає певного звикання. Гарне редагування!
Дастман

GHad: Чи читали ви мій Edit2 вище? Він стосується саме того, що ви згадали, і він був опублікований до вашого допису.
Кріс Джестер-Янг

2
Чому не list.toArray (new String [list.size ()]), а не new String [0], оскільки код буде використовувати новий масив, якщо він правильного розміру?
цинік

Так, це працює. В іншому випадку деякий код (в основному в бібліотеці класів Java) зберігає статичний екземпляр String [0] (та інші подібні масиви нульового розміру) і передає статичні екземпляри замість того, щоб кожного разу створювати один. :-)
Кріс Джестер-Янг

31

Альтернатива в Java 8:

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

Це має бути прийнятою відповіддю. Хоча і не дуже добре, враховуючи, що інші мови програмування можуть робити це з меншим і чіткішим кодом ... це найкраще, що може запропонувати Java, незалежно від іншої бібліотеки.
Джейсон

7
Хоча це і акуратно, це буде надзвичайно неефективно в порівнянні з System.arraycopy. Можливо, не слід робити це в реальному коді.
Білл К

якщо foo є динамічною рядковою змінною, це кидає помилку часу компіляції Локальна змінна foo, визначена в
обговорюючій

Питання говорить "Дано масив з 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

1
І Кріс, і Ларш мають рацію: списки з підтримкою масивів (іншими словами, ті, що створюються за допомогою Arrays.asList ()) є структурно незмінними, що повністю робить цю відповідь недійсною. Проте на сьогодні я бачу сімнадцять голосів проти.
Здивує

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

6

Ви можете використовувати зовнішню бібліотеку:

org.apache.commons.lang.ArrayUtils.remove(java.lang.Object[] array, int index)

Це в проекті Apache Commons Lang http://commons.apache.org/lang/


ArrayUtils.removeElement(boolean[] array, boolean element)також дуже корисний.
scai

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]

У своєму завданні ви можете спочатку відсканувати масив, щоб зібрати позиції "а", а потім зателефонувати removeItems().


1
Будь ласка, не роби цього. Це заплутано, повільно та схильне до помилок. Просто використовуйте System.arraycopy () замість цього - хоча бонусні бали за той факт, що якщо ви збираєтеся маніпулювати масивом таким чином, ви повинні відстежувати довжину.
Білл К

4

Тут є багато відповідей - проблема, як я бачу, полягає в тому, що ви не сказали ЧОМУ ви використовуєте масив замість колекції, тож дозвольте запропонувати кілька причин і які рішення застосовуватимуться (Більшість рішень вже отримали відповіді в інших запитаннях тут, тому я не буду вдаватися в занадто багато деталей):

причина: Ви не знали, що пакет колекцій існує, або йому не довіряли

рішення: Використовуйте колекцію.

Якщо ви плануєте додавати / видаляти з середини, використовуйте LinkedList. Якщо ви дійсно стурбовані розміром або часто індексуєте в середині колекції, використовуйте ArrayList. Обидва вони повинні мати операції видалення.

причина: Ви стурбовані розміром або хочете контролювати розподіл пам'яті

рішення: Використовуйте ArrayList із певним початковим розміром.

ArrayList - це просто масив, який може розширюватись сам, але не завжди це потрібно робити. Це буде дуже розумно щодо додавання / видалення елементів, але знову ж таки, якщо ви вставляєте / видаляєте ЛОТ із середини, використовуйте LinkedList.

причина: у вас є масив, який надходить, а масив виходить - тож ви хочете оперувати масивом

рішення: Перетворіть його в ArrayList, видаліть елемент і перетворіть назад

причина: Ви вважаєте, що можете написати кращий код, якщо зробите це самостійно

рішення: ви не можете, скористайтеся списком Array або Linked.

причина: це завдання класу, і ви з певних причин не маєте доступу або не маєте доступу до колекції apis

припущення: Вам потрібен новий масив, щоб мати правильний "розмір"

рішення: відскануйте масив на відповідні елементи та підрахуйте їх. Створіть новий масив правильного розміру (оригінальний розмір - кількість збігів). використовуйте System.arraycopy неодноразово, щоб скопіювати кожну групу елементів, які ви хочете зберегти, у свій новий масив. Якщо це призначення класу, і ви не можете використовувати System.arraycopy, просто скопіюйте їх по черзі вручну, але ніколи не робіть цього у виробничому коді, оскільки це набагато повільніше. (Ці рішення детально описані в інших відповідях)

причина: потрібно бігти оголеним металом

припущення: ви НЕ ПОВИННІ виділяти простір без потреби або займати занадто багато часу

припущення: Ви відстежуєте розмір, що використовується в масиві (довжина), окремо, оскільки в іншому випадку вам довелося б перерозподілити свій масив для видалення / вставки.

Приклад того, чому ви можете зробити це: один масив примітивів (скажімо, значення int) бере значну частину вашого оперативного пам'яті - приблизно 50%! ArrayList змусить їх увійти до списку покажчиків на об’єкти Integer, які використовуватимуть у кілька разів більше пам’яті.

рішення: Ітерація над вашим масивом, і кожного разу, коли ви знайдете елемент для видалення (назвемо його елементом n), використовуйте System.arraycopy, щоб скопіювати хвіст масиву над елементом "видалено" (джерело та призначення - це однаковий масив) достатньо розумний, щоб зробити копію у правильному напрямку, щоб пам'ять не перезаписувала себе:

 System.arraycopy (ary, n + 1, ary, n, length-n) 
 довжина--;

Напевно, ви захочете бути розумнішим, ніж це, якщо одночасно видаляєте більше одного елемента. Ви б переміщали лише область між одним "сірником" і наступним, а не цілим хвостом, і, як завжди, уникайте переміщення будь-якого шматка двічі.

В цьому останньому випадку ви абсолютно повинні виконати роботу самостійно, і використання 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

Я усвідомлюю, що це дуже стара публікація, але деякі відповіді тут мені допомогли, тож ось моя туппена `` ха'пенні ''!

Я боровся з тим, щоб це працювало досить довгий час, перш ніж перекручувати, що масив, до якого я записую, потрібно змінити, якщо зміни, внесені до ArrayListзалишення списку, не зміниться.

Якщо те, ArrayListщо ви модифікуєте, закінчується більшими або меншими елементами, ніж було розпочато, рядок List.toArray()спричинить виняток, тому вам потрібно щось на зразок List.toArray(new String[] {})або List.toArray(new String[0])для того, щоб створити масив з новим (правильним) розміром.

Звучить очевидно зараз, коли я це знаю. Не настільки очевидний для початківців Android / Java, який стикається з новими і незнайомими конструкціями коду, і не очевидний з деяких попередніх публікацій тут, тому просто хотів зробити цей пункт справді зрозумілим для тих, хто чеше голови годинами, як я !


Я відчув потребу опублікувати це, оскільки часто використовую фрагменти коду, які не працюють, бо я пропустив щось, що інші кодери сприймають як належне. GHad зазначив про розмір масиву, який змусив мій код працювати (дякую, що це зрозуміло). Випробування речей - це спосіб вчитися, і якщо це означає, що я заслуговую на все, що отримую за те, щоб взяти код із SO та спробувати зрозуміти, як / чому це працює, то нехай буде так. Як неоплачуваний любитель, я не той геній Java, яким деякі люблять думати! На щастя, більшість учасників SO відповідають на запитання, щоб допомогти іншим написати кращий код: За це ви заслуговуєте подяки!
DDSports

2

Початковий масив

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

якщо ви хочете видалити 51, що є індексом 2, використовуйте наступне

 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]

Де, як відповідає поточна прийнята відповідь Кріса Єстера Янга:

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

без жодних нульових значень.


Хороша спроба, але сигари немає. Я опублікував редагування саме на цю тему, ще до того, як ви зробили свою публікацію. Тож, хоча ви "технічно правильні", я не ціную ваших спроб змусити людей витіснити мою посаду. Я просто думав, що ви повинні це знати.
Кріс Джестер-Янг

Йдеться не про переміщення посади, а про уникнення помилок та небезпечного коду. Greetz GHad
GHad

Помилок можна уникнути, якщо люди прочитають весь мій допис (включаючи обидва додатки). Якщо люди просто вирізають і вставляють код, не замислюючись, то вони заслуговують на все, що отримують. Програмістам платять за те, що вони роблять, тому що вони вправляють свої мізки ... Я сподіваюся. [продовжує]
Кріс Джестер-Янг

1
[продовження] Ваш код теж "небезпечний", якщо люди не усвідомлюють того, що, використовуючи хеш, елементи стають невпорядкованими. Звичайно, мислячі програмісти це усвідомлюють, але якщо ви називаєте мій код небезпечним, оскільки люди вирізають і вставляють, не думаючи, справедливо сказати те саме.
Кріс Джестер-Янг

Звичайно, ви маєте рацію щодо хешу. І як зрозуміло суть вашого другого редагування, я, мабуть, перечитав це. Як вже було сказано, просто хотів уникнути масиву з нульовими значеннями та дублювання. Ви можете змінити свій код на основі вашого другого редагування та залишити коментар у Edit3 щодо масиву. Не хотів нападати на вас
GHad

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

}


Це дійсно хороша відповідь, хоча ваш чудовий тестовий код робить його набагато більшим, ніж є насправді. Повним рішенням може бути однорядкова копія масиву (припускаючи, що ви використовуєте той самий масив, який ви хотіли б зробити, якби ви працювали так близько до металу, що використовували масиви замість колекцій).
Білл К

спасибі, я написав це, щоб читачі могли трохи побачити, що відбувається під капотом, і протестувати, тепер я ще раз переглядаю це System.arraycopy (tmp1, 0, tmp2, 0, tmp2.length); може бути видалений і замінений чимось на зразок for (i = 0; i <(arr.length - t); i ++) {tmp2 [i] = tmp1 [i]; } Ви також можете створити байт-буфер і скопіювати 8 байт за раз на 64-бітній машині, щоб отримати додаткову продуктивність копіювання
Андре

0

Це залежить від того, що ви маєте на увазі під словом "видалити"? Масив - це конструкція фіксованого розміру - ви не можете змінити кількість елементів у ньому. Таким чином, ви можете: а) створити новий, коротший масив без елементів, які ви не хочете, або б) призначити записи, які ви не хочете, до чогось, що вказує на їхній статус "порожнього"; зазвичай нуль, якщо ви не працюєте з примітивами.

У першому випадку створіть Список із масиву, видаліть елементи та створіть новий масив зі списку. Якщо продуктивність важлива, переглядайте масив, призначаючи елементи, які не слід видаляти до списку, а потім створюйте новий масив зі списку. У другому випадку просто пройдіть і призначте нуль для записів масиву.


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

У масиві рядків типу

String name = 'abcdeafbde' // може бути схожим на String name = 'aa bb cde aa f bb de'

Я будую наступний клас

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) перші елементи з масиву.

Думаю, це найпростіший та ефективний спосіб використання.

PS : indexOfможе бути реалізований різними способами, це моя версія.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.