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

나는 그런 루프로 시도

// ArrayList tourists

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

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


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

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



답변

시험:

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

Java API를 읽으십시오 . 이 코드는 java.lang.UnsupportedOperationException불변 목록 (예 :로 만든 Arrays.asList)을 처리합니다. 자세한 내용 은 이 답변 을 참조하십시오.


답변

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

tourists.removeIf(Objects::isNull);

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


답변

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


답변

비효율적이지만 짧음

while(tourists.remove(null));


답변

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

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


답변

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


답변

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 작업이 아닙니다. 이론적으로 이론과 실제는 같은 것입니다. 실제로는 그렇지 않습니다.