Як видалити всі нульові елементи з ArrayList або String Array?


188

Я намагаюся з такою петлею

// ArrayList tourists

for (Tourist t : tourists) {
    if (t != null) {     
        t.setId(idForm); 
    }   
}

Але це не приємно. Хтось може запропонувати мені краще рішення?


Деякі корисні орієнтири для кращого рішення:

Під час циклу, для циклу та перевірки продуктивності ітератора


2
використовувати Iterator? Копайте java-doc. download.oracle.com/javase/6/docs/api/java/util/…
Nishant

Відповіді:


365

Спробуйте:

tourists.removeAll(Collections.singleton(null));

Прочитайте Java API . Код буде міститись java.lang.UnsupportedOperationExceptionдля незмінних списків (наприклад, створених за допомогою Arrays.asList); див. цю відповідь для отримання більш детальної інформації.


9
Часова складність List.removeAll()становить n ^ 2 . Просто кажу.
Хемант

8
Для Java 8 або новішої версії див. Відповідь @ MarcG нижче.
Енді Томас

2
@Hemanth Чи можете ви детальніше розповісти про те, як ви отримали складність у часі? Тому що це досить O(n)мені здається і на, ArrayListі на LinkedList.
Хелдер Перейра

1
@HelderPereira Я не думаю, що це повинно бути для цього випадку , оскільки джерело (рядок 349), здається, проходить через обидва списки ( циклічний contains()весь масив), а оскільки singletonє лише одним елементом, це було б N * 1 = N. Однак в цілому це було б N^2.
Мойра

6
@Hemanth Ні, це не так. Це n * m, де m - кількість елементів, в цьому випадку сингл нуля, який дорівнює 1. Це O (n). Ви можете побачити тут вихідний код і побачити, як він читає та записує список, і переміщає елементи для обліку залишеного.
Татаризація

117

Станом на 2015 рік, це найкращий спосіб (Java 8):

tourists.removeIf(Objects::isNull);

Примітка. Цей код буде java.lang.UnsupportedOperationExceptionподано для списків фіксованого розміру (наприклад, створених за допомогою Arrays.asList), включаючи незмінні списки.


1
"Найкраще" яким чином? Це швидше, ніж інші підходи? Або це просто легше читати в силу стислості?
Енді Томас

15
Не тільки через стислість, а й тому, що вона більш виразна. Ви майже можете прочитати його: "Від туристів видаліть, якщо об’єкт недійсний". Також старим способом є створення нової колекції з одним нульовим об'єктом, а потім прохання видалити вміст колекції з іншого. Здається, трохи хак, ти не думаєш? Що стосується швидкості, то ви маєте бачення, якщо список справді великий і продуктивність викликає занепокоєння, я б запропонував протестувати обидва способи. Моя здогадка, removeIfце швидше, але це здогад.
MarcG

1
Arrays.asListне є незмінним . Він фіксованого розміру.
турбанов

@turbanoff так, ти прав, звичайно. Це лише фіксованого розміру, я оновлю відповідь.
MarcG

46
list.removeAll(Collections.singleton(null));

Це буде Кидки UnsupportedException , якщо ви використовуєте його на Arrays.asList , тому що це дасть вам Незмінну копію , тому він не може бути змінений. Дивіться нижче коду. Це створює мутаційну копію і не кине жодного винятку.

public static String[] clean(final String[] v) {
    List<String> list = new ArrayList<String>(Arrays.asList(v));
    list.removeAll(Collections.singleton(null));
    return list.toArray(new String[list.size()]);
}

18

Не ефективний, але короткий

while(tourists.remove(null));

1
На жаль, ваше рішення було єдиним, яке працювало на мене ... дякую!
Pkmmte

простий і швидкий

5
@mimrahe насправді швидкий, насправді. страшно повільно, якщо у вас великий список.
Gewure

18

Якщо ви віддаєте перевагу незмінним об'єктам даних або ви просто не хочете бути руйнівними для вхідного списку, ви можете використовувати предикати Гуави.

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))

7
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }

Це може бути корисніше, коли вам доведеться видаляти елементи під час руху. Збіг обставин - це те, що я спускав нанівець елементи, ніж намагався використовувати removeAll(..null..). Дякую!
Мустафа

Можливо, вам буде краще встановити значення "null", а потім видалити їх наприкінці. BatchRemove у RemoveAll перетинає список з місцем читання та запису та повторює список один раз, переміщуючи прочитане, але не записуючи, коли воно потрапляє на нуль. .remove (), можливо, потрібно легітимно копіювати весь масив кожного разу, коли він викликається.
Татариз

4

Перед Java-8 вам слід скористатися:

tourists.removeAll(Collections.singleton(null));

Використання Post-Java 8:

tourists.removeIf(Objects::isNull);

Причина тут - складність у часі. Проблема з масивами полягає в тому, що операція з видалення може зайняти час (O) n. Дійсно в Java це копія масиву решти елементів, що переміщуються, щоб замінити порожнє місце. Багато інших запропонованих тут рішень спровокують це питання. Перший технічно O (n * m), де m дорівнює 1, тому що це одинаковий нуль: тому O (n)

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

Це ефективно робить це внутрішньо:

public static <E> void removeNulls(ArrayList<E> list) {
    int size = list.size();
    int read = 0;
    int write = 0;
    for (; read < size; read++) {
        E element = list.get(read);
        if (element == null) continue;
        if (read != write) list.set(write, element);
        write++;
    }
    if (write != size) {
        list.subList(write, size).clear();
    }
}

Яке ви можете явно бачити, це операція O (n).

Єдине, що коли-небудь може бути швидшим, це якщо ви повторили список з обох кінців, і коли ви знайшли нуль, ви встановите його значення, яке дорівнює значенню, яке ви знайшли в кінці, і зменшили це значення. І повторюється, поки два значення не збігаються. Ви б зіпсували порядок, але значно зменшите кількість встановлених вами значень порівняно з тими, які ви залишили в спокої. Який хороший метод знати, але тут не дуже допоможе, оскільки .set () в основному безкоштовний, але ця форма видалення є корисним інструментом для вашого пояса.


for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }

Хоча це здається досить розумним, .remove () на ітераторі внутрішньо викликає:

ArrayList.this.remove(lastRet);

Це знову ж таки операція O (n) у межах видалення. Він робить System.arraycopy (), який знову не є тим, що ви хочете, якщо ви дбаєте про швидкість. Це робить n ^ 2.

Також є:

while(tourists.remove(null));

Що є O (m * n ^ 2). Тут ми не лише повторюємо список. Ми повторюємо весь список, кожен раз, коли ми відповідаємо нулю. Тоді ми робимо n / 2 (середні) операції, щоб зробити System.arraycopy () для виконання видалення. Ви могли б абсолютно дослівно сортувати всю колекцію між предметами зі значеннями та предметами з нульовими значеннями та обрізати закінчення за менший час. Насправді це справедливо для всіх зламаних. Принаймні теоретично, фактична system.arraycopy насправді не є N операцією на практиці. В теорії теорія і практика - це одне і те ж; на практиці це не так.


3

Існує простий спосіб видалення всіх nullзначень з. collectionВам потрібно передати колекцію, що містить null як параметр removeAll()методу

List s1=new ArrayList();
s1.add(null);

yourCollection.removeAll(s1);

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

3

ObjectsКлас має , nonNull Predicateякий може бути використаний зfilter .

Наприклад:

tourists.stream().filter(Objects::nonNull).collect(Collectors.toList());

1
Ласкаво просимо до переповнення стека. Відповідаючи на запитання, будь ласка, спробуйте додати пояснення свого коду. Поверніться та відредагуйте свою відповідь, щоб включити більше інформації.
Тайлер

3

Використовуючи Java 8, ви можете це зробити за допомогою stream()іfilter()

tourists = tourists.stream().filter(t -> t != null).collect(Collectors.toList())

або

tourists = tourists.stream().filter(Objects::nonNull).collect(Collectors.toList())

Для отримання додаткової інформації: Java 8 - Потоки


1
Це рішення працює з незмінною копією, тобто -> Список <String> listOfString = Arrays.asList ("test1", null, "test"); ..... теж! Спасибі
Anurag_BEHS

2

Це простий спосіб видалити з масиву архівних значень за замовчуванням нульові значення

     tourists.removeAll(Arrays.asList(null));  

інакше значення String "null" видалити з масиву

       tourists.removeAll(Arrays.asList("null"));  

1

Я пограв з цим і дізнався, що trimToSize (), здається, працює. Я працюю на платформі Android, щоб це могло бути інакше.


2
За словами javadoc, trimToSizeне змінює вміст а ArrayList. Якщо в android це інше, це, ймовірно, помилка.
fabian

1

Ми можемо використовувати ітератор для того ж, щоб видалити всі нульові значення.

Iterator<Tourist> itr= tourists.iterator();
while(itr.hasNext()){
    if(itr.next() == null){
        itr.remove();
    }
}

1

Я використовував інтерфейс потоку разом із операцією збирання потоку та методом помічника для створення нового списку.

tourists.stream().filter(this::isNotNull).collect(Collectors.toList());

private <T> boolean isNotNull(final T item) {
    return  item != null;
}

2
tourists.stream().filter(s -> s != null).collect(Collectors.toList());
1ac0

1

В основному я використовую це:

list.removeAll(Collections.singleton(null));

Але після того, як я дізнався Java 8, я перейшов до цього:

List.removeIf(Objects::isNull);

0

За допомогою Java 8 це можна виконати різними способами, використовуючи потоки, паралельні потоки та removeIfметод:

List<String> stringList = new ArrayList<>(Arrays.asList(null, "A", "B", null, "C", null));
List<String> listWithoutNulls1 = stringList.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
List<String> listWithoutNulls2 = stringList.parallelStream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
stringList.removeIf(Objects::isNull); //[A,B,C]

Паралельний потік використовуватиме доступні процесори та прискорить процес для списків розумного розміру. Завжди бажано орієнтуватися перед використанням потоків.


0

Подібно до @Lithium відповіді, але не видає помилку "Список не містить типу null":

   list.removeAll(Collections.<T>singleton(null));

0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.