ArrayList 또는 String Array에서 모든 null 요소를 제거하는 방법은 무엇입니까?


188

나는 그런 루프로 시도

// ArrayList tourists

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

그러나 좋지 않습니다. 누구든지 나에게 더 나은 해결책을 제안 할 수 있습니까?


더 나은 의사 결정을위한 유용한 벤치 마크 :

While 루프, For 루프 및 반복자 성능 테스트


2
사용 Iterator? 발굴 자바 문서. 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 입니다. 그냥 말하면
Hemanth

8
Java 8 이상에 대해서는 아래 @MarcG의 답변을 참조하십시오.
Andy Thomas

2
@Hemanth 시간이 얼마나 복잡해 지는지 자세히 설명해 주시겠습니까? 그것은 아주보기 때문에 O(n)모두 나에게 ArrayListLinkedList.
Helera Pereira

1
@HelderPereira 나는 소스 (349 행)가 두 목록 을 반복하는 것처럼 보이고 ( 전체 배열을 반복합니다) 왜냐하면 하나의 요소 이기 때문에이 경우에는 그렇지 않아야한다고 생각합니다 . 그러나 일반적으로 그렇습니다 . contains()singletonN * 1 = NN^2
Moira

6
@Hemanth 아닙니다. n * m입니다. 여기서 m 은이 경우 요소 수이며 null은 1입니다 .1입니다 .O (n)입니다. 여기에서 소스 코드를 볼 수 있고 목록을 한 번 읽고 읽고 remvoed 코드를 설명하기 위해 요소를 이동시키는 것을 볼 수 있습니다.
Tatarize

117

2015 년 현재 이것이 가장 좋은 방법입니다 (Java 8).

tourists.removeIf(Objects::isNull);

참고 : 이 코드는 java.lang.UnsupportedOperationException불변 목록을 포함하여 고정 크기 목록 (예 : Arrays.asList로 생성)에 대해 발생합니다.


1
어떤 방법으로 "최고"? 다른 접근 방식보다 빠릅니까? 아니면 간결함으로 더 읽기 쉬울까요?
Andy Thomas

15
간결함뿐만 아니라 표현력이 뛰어 나기 때문입니다. 당신은 거의 읽을 수 있습니다 : "관광객에서 객체가 null 인 경우 제거". 또한 오래된 방법은 단일 null 객체로 새 컬렉션을 만든 다음 다른 컬렉션에서 컬렉션의 내용을 제거하도록 요청하는 것입니다. 약간의 해킹처럼 보입니다. 속도와 관련하여 목록이 실제로 크고 성능이 우려되는 경우 두 가지 방법을 모두 테스트하는 것이 좋습니다. 내 추측 removeIf은 더 빠를 것이지만 추측입니다.
MarcG

1
Arrays.asList없는 불변 . 고정 크기입니다.
turbanoff

@ turbanoff 네, 물론입니다. 크기가 고정되어 있으며 답변을 업데이트하겠습니다.
MarcG

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

그것은 것이다 예외 UnsupportedException를 당신에게 제공하기 때문에 당신이 Arrays.asList에 그것을 사용하는 경우 불변 이 변경 될 수 없도록 사본을. 아래 코드를 참조하십시오. Mutable 사본을 작성 하고 예외를 발생시키지 않습니다.

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

불변의 데이터 객체를 선호하거나 입력 목록을 파괴하고 싶지 않은 경우 Guava의 술어를 사용할 수 있습니다.

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

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

순회하는 동안 요소를 삭제해야 할 때 더 유용 할 수 있습니다. 우연의 일치는 사용하려고보다 요소를 무효화했다는 것 removeAll(..null..)입니다. 감사!
Mustafa

값을 null로 설정 한 다음 끝에 제거하는 것이 좋습니다. removeAll의 batchRemove는 읽기 및 쓰기 위치를 사용하여 목록을 가로 지르고 목록을 한 번 반복하여 읽기는하지만 널에 도달하면 쓰기는 이동하지 않습니다. .remove () 호출 될 때마다 전체 배열을 합법적으로 복사해야 할 수도 있습니다.
Tatarize

4

Java 8 이전 버전을 사용해야합니다.

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

Java 8 이후 사용 :

tourists.removeIf(Objects::isNull);

그 이유는 시간 복잡성 때문입니다. 배열의 문제점은 제거 작업을 완료하는 데 O (n) 시간이 걸릴 수 있다는 것입니다. 실제로 Java에서는 비어있는 지점을 대체하기 위해 이동하는 나머지 요소의 배열 사본입니다. 여기에 제공된 다른 많은 솔루션이이 문제를 유발합니다. 전자는 기술적으로 O (n * m)입니다. 여기서 싱글 톤 널이므로 m은 1입니다. 따라서 O (n)

내부적으로 모든 singleton을 제거해야하며 내부적으로 읽기 위치와 쓰기 위치가있는 batchRemove ()를 수행합니다. 그리고 목록을 반복합니다. null에 도달하면 단순히 읽기 위치를 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) 연산입니다.

더 빠른 유일한 방법은 목록을 양쪽 끝에서 반복하고 null을 찾았을 때 그 값을 끝에서 찾은 값과 동일하게 설정하고 그 값을 줄였다는 것입니다. 그리고 두 값이 일치 할 때까지 반복했습니다. 순서를 엉망으로 만들지 만 설정 한 값의 수와 혼자 남겨둔 값의 수를 크게 줄입니다. .set ()이 기본적으로 무료이기 때문에 알기에 좋은 방법이지만 여기서는 많이 도움이되지 않지만 삭제 형식은 벨트에 유용한 도구입니다.


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

이것이 충분히 합리적인 것처럼 보이지만 반복자의 .remove ()는 내부적으로 다음을 호출합니다.

ArrayList.this.remove(lastRet);

이것은 다시 remove 내의 O (n) 연산입니다. 속도에 신경 쓰면 다시 원하지 않는 System.arraycopy ()를 수행합니다. 이것은 n ^ 2가됩니다.

또한있다 :

while(tourists.remove(null));

어느 것이 O (m * n ^ 2)입니다. 여기서 우리는 목록을 반복 할뿐만 아니라 null과 일치 할 때마다 전체 목록을 반복합니다. 그런 다음 n / 2 (평균) 작업을 수행하여 System.arraycopy ()를 수행하여 제거를 수행합니다. 말 그대로 값이있는 항목과 null 값이있는 항목 사이의 전체 컬렉션을 정렬하고 더 적은 시간 안에 끝을자를 수 있습니다. 사실, 그것은 깨진 모든 사람에게 해당됩니다. 적어도 이론 상으로는 실제 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
스택 오버플로에 오신 것을 환영합니다. 질문에 답할 때 코드에 대한 설명을 추가하십시오. 더 많은 정보를 포함하도록 돌아가서 답을 편집하십시오.
Tyler

3

Java 8을 사용하면 stream()and를 사용 하여이 작업을 수행 할 수 있습니다filter()

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

또는

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

자세한 정보 : Java 8-Streams


1
이 솔루션은 변경 불가능한 사본, 즉-> List <String> listOfString = Arrays.asList ( "test1", null, "test"); ..... 너무! 감사합니다
Anurag_BEHS

2

이것은 arraylist에서 기본 null 값을 제거하는 쉬운 방법입니다

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

그렇지 않으면 문자열 값 "null"이 arraylist에서 제거됩니다.

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

1

나는 이것을 가지고 놀았고 trimToSize ()가 작동하는 것으로 나타났습니다. Android 플랫폼에서 작업 중이므로 다를 수 있습니다.


2
javadoc에 따르면 trimToSize의 내용을 수정하지 마십시오 ArrayList. 이것이 안드로이드에서 다르면 아마도 버그 일 것입니다.
fabian

1

반복자를 동일하게 사용하여 모든 null 값을 제거 할 수 있습니다.

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);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.