Как удалить объекты из массива в Java? - PullRequest
75 голосов
/ 22 сентября 2008

Учитывая массив n объектов, допустим, что это массив строк , и он имеет следующие значения:

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

Что мне нужно сделать, чтобы удалить / удалить все строки / объекты, равные "a" в массиве?

Ответы [ 19 ]

104 голосов
/ 22 сентября 2008

[Если вам нужен готовый код, перейдите к моему «Edit3» (после вырезки). Остальное здесь для потомков.]

Чтобы конкретизировать Идея Дастмена :

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: вышеупомянутый подход сохраняет тот же массив (таким образом, массив все еще той же длины); элемент после последнего установлен в нуль. Если вы хотите, чтобы новый массив имел точно такой же размер, используйте это вместо:

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 ed при каждом вызове вашей функции.

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

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

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

27 голосов
/ 21 апреля 2014

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

String[] filteredArray = Arrays.stream(array)
    .filter(e -> !e.equals(foo)).toArray(String[]::new);
20 голосов
/ 22 сентября 2008

Создайте List из массива с помощью Arrays.asList() и вызовите remove() для всех соответствующих элементов. Затем вызовите toArray() в «Списке», чтобы снова вернуться в массив.

Не очень производительно, но если вы правильно его инкапсулируете, вы всегда сможете сделать что-то быстрее.

15 голосов
/ 23 сентября 2008

Вы всегда можете сделать:

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 голосов
/ 04 февраля 2011

Вы можете использовать внешнюю библиотеку:

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

Он находится в проекте Apache Commons Lang http://commons.apache.org/lang/

5 голосов
/ 27 января 2014

См. Код ниже

ArrayList<String> a = new ArrayList<>(Arrays.asList(strings));
a.remove(i);
strings = new String[a.size()];
a.toArray(strings);
4 голосов
/ 10 мая 2015

Если вам нужно удалить несколько элементов из массива, не преобразовывая его в 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().

3 голосов
/ 22 сентября 2008

Что-то в списке создания, затем удалении и возвращении в массив кажется мне неправильным. Не проверял, но я думаю, что следующее будет работать лучше. Да, я, вероятно, излишне предварительно оптимизирую.

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];
   }
}
2 голосов
/ 05 июля 2013

Я понимаю, что это очень старая публикация, но некоторые ответы здесь помогли мне, так что вот ценность моего happensny 'tupence!

Я долго пытался заставить это работать, прежде чем попытался изменить размер массива, в который я записываю данные, если только изменения, внесенные в ArrayList, не оставили размер списка неизменным.

Если изменяемый вами ArrayList заканчивается большим или меньшим количеством элементов, чем было в начале, строка List.toArray() вызовет исключение, поэтому вам нужно что-то вроде List.toArray(new String[] {}) или List.toArray(new String[0]), чтобы создать массив с новым (правильным) размером.

Кажется очевидным, теперь, когда я это знаю. Не так очевидно для новичка в Android / Java, который разбирается с новыми и незнакомыми конструкциями кода, и не очевидно из некоторых предыдущих постов здесь, поэтому просто хотел, чтобы этот момент был действительно ясным для всех, кто часами ломает голову, как я !

2 голосов
/ 24 мая 2017

Здесь есть много ответов - проблема, как я понимаю, состоит в том, что вы не сказали, ПОЧЕМУ вы используете массив вместо коллекции, поэтому позвольте мне предложить пару причин и какие решения будут применяться (Большинство Ответы на другие вопросы уже даны здесь, поэтому я не буду вдаваться в подробности):

причина: вы не знали, что пакет сбора существует, или не доверяли ему

решение: использовать коллекцию.

Если вы планируете добавлять / удалять из середины, используйте LinkedList. Если вы действительно беспокоитесь о размере или часто индексируете прямо в середине коллекции, используйте ArrayList. У обоих из них должны быть операции удаления.

причина: Вы обеспокоены размером или хотите контролировать выделение памяти

решение: используйте ArrayList с определенным начальным размером.

ArrayList - это просто массив, который может расширяться, но это не всегда нужно делать. Он будет очень умным при добавлении / удалении элементов, но опять же, если вы вставляете / удаляете LOT из середины, используйте LinkedList.

причина: у вас есть входящий массив и выходящий массив - поэтому вы хотите работать с массивом

решение: преобразовать его в ArrayList, удалить элемент и преобразовать обратно

причина: вы думаете, что можете написать лучший код, если вы сделаете это самостоятельно

решение: вы не можете использовать массив или связанный список.

причина: это назначение класса, и вам запрещено или у вас нет доступа к коллекции apis по какой-то причине

допущение: новый массив должен иметь правильный размер

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

причина: вам нужно запустить голый металл

предположение: вы НЕ ДОЛЖНЫ распределять пространство без необходимости или занимает слишком много времени

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

Пример того, почему вы можете захотеть сделать это: один массив примитивов (скажем, значения int) занимает значительную часть вашего барана - например, 50%! ArrayList вынуждает их составлять список указателей на объекты Integer, которые будут использовать в несколько раз больше памяти.

решение: перебирайте массив, и всякий раз, когда вы найдете удаляемый элемент (назовем его элементом n), используйте System.arraycopy, чтобы скопировать хвост массива поверх «удаленного» элемента (Source и Destination - это один и тот же массив) - он достаточно умен, чтобы сделать копию в правильном направлении, чтобы память не перезаписывала себя:

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

Возможно, вы захотите быть умнее этого, если удаляете более одного элемента одновременно. Вы должны перемещать область между одним «матчем» и следующим, а не всем хвостом, и, как всегда, избегать перемещения любого куска дважды.

В этом последнем случае вы обязательно должны выполнить работу самостоятельно, и использование System.arraycopy на самом деле является единственным способом сделать это, поскольку он выберет наилучший из возможных способов перемещения памяти для архитектуры вашего компьютера - это должно быть во много раз быстрее любого кода, который вы могли бы написать самостоятельно.

...