2011-01-27 49 views
151

tôi cố gắng với một vòng lặp như thếLàm thế nào để loại bỏ tất cả các phần tử null từ một ArrayList hoặc String Array?

// ArrayList tourists 

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

Nhưng nó không phải là tốt đẹp. Bất cứ ai có thể gợi ý cho tôi một giải pháp tốt hơn?


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

While loop, For loop and Iterator Performance Test

+2

sử dụng 'Iterator'? Đào java-doc. http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html#remove%28%29 – Nishant

Trả lời

317

Hãy thử:

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

Đọc Java API. Mã sẽ ném java.lang.UnsupportedOperationException cho các danh sách bất biến (chẳng hạn như được tạo với Arrays.asList); xem this answer để biết thêm chi tiết.

+0

Chi tiết triển khai nhỏ: Cuộc gọi này thay đổi dung lượng của Danh sách. –

+5

Độ phức tạp thời gian của 'List.removeAll()' là ** n^2 **. Chỉ cần nói. – Hemanth

+4

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

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

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

+0

Bạn có thể nên tắt các giá trị thành null sau đó xóa ở cuối. BatchRemove trong removeAll chuyển đổi danh sách, với một 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 một null. .remove() có thể legit phải arraycopy toàn bộ mảng mỗi khi nó được gọi. – Tatarize

17

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! – Pkmmte

+0

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

+0

tuyệt vời! Hãy yêu thích nó! – Flax

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ư làm việc. Tôi đang làm việc trên nền tảng Android để có thể khác.

+2

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

3

Có một cách dễ dàng để loại bỏ tất cả các giá trị từ nullcollection .Bạn phải vượt qua một bộ sưu tập có chứa null như một tham số để removeAll() phương pháp

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

yourCollection.removeAll(s1); 
+0

Đ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 hơn một mục trong "mảng bộ lọc" của bạn được chuyển vào phương thức removeAll của bộ sưu tập gốc. –

16

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

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull())) 
42
list.removeAll(Collections.singleton(null)); 

It will Throws UnsupportedException if you use it on Arrays.asList because it give you Immutable copy so it can not be modified. See below the code. It creates Mutable copy and will not throw any exception.

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

Đây là cách dễ dàng để loại bỏ các giá trị null mặc định từ ArrayList

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

khác String value "null" loại bỏ khỏi ArrayList

 tourists.removeAll(Arrays.asList("null")); 
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(); 
    } 
} 
71

Tính đến 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.UnsupportedOperationException cho các danh sách kích thước cố định (ví dụ như tạo ra với Arrays.asList), bao gồm danh sách bất biến.

+1

"Tốt nhất" theo cách nào? Nó có nhanh hơn các cách tiếp cận khác không? Hay là nó dễ đọc hơn bởi đức tính ngắn gọn? –

+7

Không chỉ vì ngắn gọn, mà còn vì nó mang tính biểu cảm hơn. Bạn gần như có thể đọc nó: "Từ khách du lịch, loại bỏ nếu đối tượng là null". Ngoài ra, cách cũ là tạo ra 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 để loại bỏ các nội dung của một bộ sưu tập từ khác. Có vẻ như một chút của một hack, bạn không nghĩ? Về tốc độ, bạn có một điểm, nếu danh sách là 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 là 'removeIf' nhanh hơn, nhưng đó là đoán. – MarcG

+1

'Arrays.asList' không phải là _immutable_. Nó có kích thước cố định. – turbanoff

1

tôi đã sử dụng giao diện dòng cùng với những hoạt động dòng thu thập và một helper-phương pháp để tạo ra một danh sách mới.

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

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

'visitor.stream(). Filter (s -> s! = Null) .collect (Collectors.toList());' – 1ac0

+0

@LadislavDANKO hoặc 'Objects :: nonNull' cho giá trị ngữ nghĩa. – Moira

3

Lớp ObjectsnonNullPredicate có 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 giải thích 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

2

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

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

hoặc

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

Để biết thêm thông: Java 8 - Streams

1

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

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

Post-Java 8 sử dụng:

tourists.removeIf(Objects::isNull); 

Lý do ở đây là thời gian phức tạp. Vấn đề với mảng là một thao tác loại 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 mảng của các yếu tố còn lại được di chuyển để thay thế chỗ trống. Nhiều giải pháp khác được cung cấp ở đây sẽ kích hoạt vấn đề này. Trước đây là kỹ thuật O (n * m) trong đó m là 1 bởi vì nó là một null đơn: vì vậy O (n)

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

Nó có hiệu quả thực hiện đ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 một cách rõ ràng có thể thấy là một O (n) hoạt động.

Điều duy nhất có thể nhanh hơn nếu bạn lặp lại danh sách từ cả hai đầu và khi bạn tìm thấy giá trị rỗng, bạn đặt giá trị bằng giá trị bạn tìm thấy ở cuối và giảm giá trị đó. Và được lặp lại cho đến khi hai giá trị khớp nhau. Bạn sẽ làm rối trật tự, nhưng sẽ làm giảm đáng kể số lượng giá trị bạn đặt so với số bạn đã để lại một mình. Đó là một phương pháp tốt để biết nhưng sẽ không giúp đỡ nhiều ở đây như .set() là cơ bản 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ẻ như đủ hợp lý, các lệnh .remove() trên iterator trong nội bộ gọi:

ArrayList.this.remove(lastRet); 

Mà lại là O (n) hoạt động trong phạm vi loại bỏ. Nó có một System.arraycopy() mà lại không phải là những gì 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 giá trị rỗng. Sau đó, chúng tôi thực hiện các thao tác n/2 (trung bình) để thực hiện System.arraycopy() để thực hiện thao tác xóa. Bạn có thể hoàn toàn theo nghĩa đen, sắp xếp toàn bộ bộ sưu tập giữa các mục có giá trị và các mục có giá trị null và cắt kết thúc trong ít thời gian hơn. Trong thực tế, đó là sự thật cho tất cả những người bị hỏng. Ít nhất trong lý thuyết, hệ thống thực tế.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 đều giống nhau; trong thực tế thì không.

0

Sử dụng Java 8 này có thể được thực hiện theo những cách khác nhau sử dụng suối, suối song song và removeIf phương pháp:

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] 

Các dòng song song sẽ sử dụng bộ vi xử lý có sẵn và sẽ đẩy nhanh quá trình cho hợp lý có kích thước danh sách. Nó luôn luôn được khuyến khích để chuẩn trước khi sử dụng dòng.

0

Tương tự như @Lithium câu trả lời nhưng không ném một "Danh sách có thể không chứa loại null" lỗi:

list.removeAll(Collections.<T>singleton(null)); 
Các vấn đề liên quan