Làm cách nào để xóa tất cả các phần tử null khỏi ArrayList hoặc String Array?


187

Tôi thử với một vòng lặp như thế

// ArrayList tourists

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

Nhưng nó không tốt Bất cứ ai có thể đề nghị cho tôi một giải pháp tốt hơn?


Một số điểm chuẩn hữu ích để đưa ra quyết định tốt hơn:

Vòng lặp while, Kiểm tra hiệu năng vòng lặp và vòng lặp


2
sử dụng Iterator? Đào java-doc. download.oracle.com/javase/6/docs/api/java/util/ từ
Nishant

Câu trả lời:


365

Thử:

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

Đọc API Java . Mã sẽ ném java.lang.UnsupportedOperationExceptioncho các danh sách bất biến (như được tạo bằng Arrays.asList); xem câu trả lời này để biết thêm chi tiết


9
Thời gian phức tạp của List.removeAll()n ^ 2 . Chỉ cần nói.
Hemanth

8
Đối với Java 8 trở lên, hãy xem câu trả lời của @ MarcG bên dưới.
Andy Thomas

2
@Hemanth Bạn có thể giải thích làm thế nào bạn có được sự phức tạp thời gian đó? Bởi vì nó trông khá O(n)cho tôi cho cả hai ArrayListLinkedList.
Pereira

1
@HelderPereira Tôi không nghĩ nó nên dành cho trường hợp này , vì nguồn (dòng 349) dường như lặp qua cả hai danh sách ( contains()vòng lặp toàn bộ mảng) và vì đó singletonchỉ là một yếu tố N * 1 = N. Tuy nhiên nhìn chung nó sẽ được N^2.
Moira

6
@Hemanth Không, không. Đó là n * m trong đó m là số phần tử trong trường hợp này là một số đơn vị null là 1. Đó là O (n). Bạn có thể xem mã nguồn ở đây và thấy nó đọc và ghi lại danh sách một lần, di chuyển các yếu tố sang tài khoản cho mã được phát lại.
Tatarize

116

Kể từ năm 2015, đây là cách tốt nhất (Java 8):

tourists.removeIf(Objects::isNull);

Lưu ý: Mã này sẽ ném java.lang.UnsupportedOperationExceptioncho các danh sách có kích thước cố định (chẳng hạn như được tạo bằng Arrays.asList), bao gồm các danh sách không thay đổi.


1
"Tốt nhất" theo cách nào? Có nhanh hơn các phương pháp khác không? Hay nó chỉ dễ đọc hơn nhờ tính ngắn gọn?
Andy Thomas

15
Không chỉ vì ngắn gọn, mà bởi vì nó biểu cảm hơn. Bạn gần như có thể đọc nó: "Từ khách du lịch, hãy xóa nếu đối tượng là null". Ngoài ra, cách cũ là tạo một bộ sưu tập mới với một đối tượng null duy nhất và sau đó yêu cầu xóa nội dung của bộ sưu tập khỏi mục kia. Có vẻ như một chút hack, bạn không nghĩ sao? Về tốc độ, bạn có một điểm, nếu danh sách thực sự lớn và hiệu suất là một mối quan tâm, tôi sẽ đề nghị thử nghiệm cả hai cách. Tôi đoán nó sẽ removeIfnhanh hơn, nhưng đó là một phỏng đoán.
MarcG 6/2/2016

1
Arrays.asListkhông phải là bất biến . Đó là kích thước cố định.
turbanoff

@turbanoff vâng, bạn đúng, tất nhiên. Chỉ có kích thước cố định, tôi sẽ cập nhật câu trả lời.
MarcG

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

Nó sẽ ném UnsupportedException nếu bạn sử dụng nó trên Arrays.asList vì nó cung cấp cho bạn bản sao bất biến để không thể sửa đổi. Xem bên dưới mã. Nó tạo ra bản sao Mutable và sẽ không ném bất kỳ ngoại lệ nào.

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

Không hiệu quả, nhưng ngắn

while(tourists.remove(null));

1
Thật không may, giải pháp của bạn là người duy nhất làm việc cho tôi ... cảm ơn!
LOLmmte

đơn giản và nhanh chóng

5
@mimrahe ngược lại với nhanh, thực sự. chậm khủng khiếp nếu bạn có một danh sách lớn.
Gewure

18

Nếu bạn thích các đối tượng dữ liệu bất biến hoặc nếu bạn không muốn phá hủy danh sách đầu vào, bạn có thể sử dụng các vị từ của Guava.

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

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

Điều này có thể hữu ích hơn khi bạn phải xóa các yếu tố trong khi di chuyển ngang. Điều trùng hợp là tôi đã vô hiệu hóa các yếu tố hơn là cố gắng sử dụng removeAll(..null..). Cảm ơn!
Mustafa

Bạn có thể tốt hơn nên đặt các giá trị thành null sau đó xóa ở cuối. LôRemove trong remove ALL chuyển đổi danh sách, với vị trí đọc và ghi và lặp lại danh sách một lần, di chuyển đọc nhưng không ghi khi nó chạm null. .remove () có thể hợp pháp phải phân mảng toàn bộ mảng mỗi lần nó được gọi.
Tatarize

4

Pre-Java 8 bạn nên sử dụng:

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

Sử dụng sau Java 8:

tourists.removeIf(Objects::isNull);

Lý do ở đây là thời gian phức tạp. Vấn đề với mảng là một hoạt động gỡ bỏ có thể mất thời gian O (n) để hoàn thành. Thực sự trong Java đây là một bản sao của các phần tử còn lại đang được di chuyển để thay thế vị trí trống. Nhiều giải pháp khác được cung cấp ở đây sẽ kích hoạt vấn đề này. Cái trước là về mặt kỹ thuật O (n * m) trong đó m là 1 vì nó là một đơn vị null: vì vậy O (n)

Bạn nên loại bỏTất cả các singleton, bên trong nó thực hiện một batchRemove () có vị trí đọc và vị trí ghi. Và lặp lại danh sách. Khi nó đạt null, nó chỉ đơn giản lặp lại vị trí đọc bằng 1. Khi chúng giống nhau, khi chúng khác nhau, nó tiếp tục di chuyển dọc theo sao chép các giá trị. Sau đó, ở cuối nó cắt theo kích thước.

Nó thực sự làm điều này trong nội bộ:

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

Mà bạn có thể thấy rõ ràng là một hoạt động O (n).

Điều duy nhất có thể nhanh hơn là nếu bạn lặp lại danh sách từ cả hai đầu và khi bạn tìm thấy null, bạn đặt giá trị của nó bằng với giá trị bạn tìm thấy ở cuối và giảm giá trị đó. Và lặp đi lặp lại cho đến khi hai giá trị khớp nhau. Bạn đã làm xáo trộn trật tự, nhưng sẽ giảm đáng kể số lượng giá trị bạn đặt so với giá trị bạn để lại một mình. Đây là một phương pháp tốt để biết nhưng sẽ không giúp ích nhiều ở đây vì .set () về cơ bản là miễn phí, nhưng hình thức xóa đó là một công cụ hữu ích cho vành đai của bạn.


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

Trong khi điều này có vẻ đủ hợp lý, .remove () trên iterator gọi nội bộ:

ArrayList.this.remove(lastRet);

Đó là một lần nữa hoạt động O (n) trong loại bỏ. Nó thực hiện một System.arraycopy () một lần nữa không phải là điều bạn muốn, nếu bạn quan tâm đến tốc độ. Điều này làm cho nó n ^ 2.

Ngoài ra còn có:

while(tourists.remove(null));

Đó là O (m * n ^ 2). Ở đây chúng tôi không chỉ lặp lại danh sách. Chúng tôi nhắc lại toàn bộ danh sách, mỗi lần chúng tôi khớp với null. Sau đó, chúng tôi thực hiện n / 2 (trung bình) để thực hiện System.arraycopy () để thực hiện xóa. Bạn hoàn toàn có thể theo nghĩa đen, sắp xếp toàn bộ bộ sưu tập giữa các mục với các giá trị và các mục có giá trị null và cắt kết thúc trong thời gian ngắn hơn. Trong thực tế, điều đó đúng cho tất cả những người bị hỏng. Ít nhất là trên lý thuyết, thực tế system.arraycopy không thực sự là một hoạt động N trong thực tế. Về lý thuyết, lý thuyết và thực hành là như nhau; trong thực tế họ không.


3

Có một cách dễ dàng để xóa tất cả các nullgiá trị khỏi collection. Bạn phải chuyển một bộ sưu tập có chứa null làm tham số cho removeAll()phương thức

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

yourCollection.removeAll(s1);

Điều này làm việc tốt nhất cho tôi. Nó cũng cho phép bạn dễ dàng thêm nhiều mục trong "mảng bộ lọc" được truyền vào phương thức removeAll của bộ sưu tập gốc.

3

Các Objectslớp có một nonNull Predicatecó thể được sử dụng với filter.

Ví dụ:

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

1
Chào mừng bạn đến với Stack Overflow. Khi trả lời câu hỏi, vui lòng thử thêm phần giải thích về mã của bạn. Vui lòng quay lại và chỉnh sửa câu trả lời của bạn để bao gồm thêm thông tin.
Tyler

3

Sử dụng Java 8, bạn có thể làm điều này bằng cách sử dụng stream()filter()

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

hoặc là

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

Để biết thêm thông tin: Java 8 - Luồng


1
Giải pháp này đang hoạt động với bản sao bất biến tức là -> Danh sách <String> listOfString = Arrays.asList ("test1", null, "test"); ..... quá ! Cảm ơn
Anurag_BEHS

2

Đây là cách dễ dàng để loại bỏ các giá trị null mặc định khỏi danh sách mảng

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

mặt khác, chuỗi giá trị "null" xóa khỏi danh sách mảng

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

1

Tôi đã chơi xung quanh với điều này và phát hiện ra rằng trimToSize () dường như hoạt động. Tôi đang làm việc trên nền tảng Android nên có thể khác.


2
Theo javadoc, trimToSizekhông sửa đổi nội dung của a ArrayList. Nếu điều này là khác nhau trong Android, nó có thể là một lỗi.
fabian

1

Chúng ta có thể sử dụng iterator cho cùng để loại bỏ tất cả các giá trị null.

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

1

Tôi đã sử dụng giao diện luồng cùng với thu thập hoạt động luồng và phương thức trợ giúp để tạo danh sách mới.

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

Chủ yếu tôi đang sử dụng cái này:

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

Nhưng sau khi tôi học Java 8, tôi đã chuyển sang:

List.removeIf(Objects::isNull);

0

Sử dụng Java 8, điều này có thể được thực hiện theo nhiều cách khác nhau bằng cách sử dụng các luồng, luồng song song và removeIfphương thức:

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]

Luồng song song sẽ sử dụng các bộ xử lý có sẵn và sẽ tăng tốc quá trình cho các danh sách có kích thước hợp lý. Nó luôn luôn được khuyến khích để điểm chuẩn trước khi sử dụng luồng.


0

Tương tự như câu trả lời @Lithium nhưng không đưa ra lỗi "Danh sách có thể không chứa loại 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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.