자바(SE)

List에서의 자료 삭제

  • -

List에서의 자료 삭제

이번 포스트에서는 List에서 데이터 순회 과정에서 자료를 삭제하는 방법과 주의할 사항에 대해 알아보자.

 

일반적인 반복문에서의 삭제

index를 이용한 for 탐색 과정에서 특정 요소를 삭제하는 과정에서는 index의 조절에 대해 신경 써줘야 한다. 아래와 같이 문자열을 관리하는 List에서 길이 기반으로 삭제하는 메서드가 있다고 생각해보자.

static List<String> list;

static void init() {
    list = new ArrayList<>();
    list.add("Hello");
    list.add("Java");
    list.add("GoGo");
    list.add("World");
}

static void indexBaseDelete(int len) {
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).length() == len) {
            list.remove(list.get(i));
        }
    }
    System.out.println(list);
}

 

만약 길이 4인 요소를 지운다고 생각해보면 결과가 어떻게 될까?

public static void main(String[] args) {
    init();
    indexBaseDelete(4);
}
    
// 결과: [Hello, GoGo, World]

 

생각과 달리 GoGo는 삭제되지 않음을 알 수 있다. Java가 삭제되면서 1번 요소가 삭제되고 원래 2번이었던 GoGo가 1번으로 내려 간 상황에서 i는 다음으로 넘어가기 때문이다.  따라서 이런 경우는 index를 차감 시켜줘야 한다.

    static void indexBaseDelete(int len) {

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).length() == len) {
                list.remove(list.get(i));
                i--;
            }
        }
        System.out.println(list);
    }

 

index 조정이 쉽지 않다면

인덱스를 조절하는 절차가 번거롭다면 Iterator를 사용할 수 있다. Iterator는 Collection에 있는 메서드이기 때문에 List는 물론 Set 계열에서도 사용이 가능하다.

    static void byIterator(int len) {
        Iterator<String> iter = list.iterator();
        while (iter.hasNext()) {
            String item = iter.next();
            if (item.length() == len) {
                iter.remove();
            }
        }
        System.out.println(list);
    }

 

 

foreach에서의 삭제

foreach 문장은 잘 알다시피 index가 없다. 따라서 index를 조절할 방법이 없기 때문에 기본적으로 위와 같은 연산을 할 수 없다.

    static void forEachBaseDelete(int len) {
        for (String str : list) {
            if (str.length() == len) {
                list.remove(str);
            }
        }
    }

이 경우 ConcurrentModificationException이 발생하면서 삭제가 안된다고 이야기 한다. foreach의 경우 read only 라고 보는 것이 맞다.

 

foreach에서의 삭제 - 어쩔때는 되는거 같던데요?

물론 이 과정에서 "그냥 하나만 삭제하고 끝낼꺼다!" 라는 생각으로 return 처리를 한다면 index 체크를 하지 않기 때문에 성공한다. 하지만 애초의 목적은 2개의 데이터가 삭제되는 것이지만 실제로는 3개가 남아있기 때문에 비지니스 로직으로는 실패다. 그냥 오류가 안난다는 이야기이다. "되는데요??" 라는 말 안나오길...

    static void forEachBaseDelete(int len) {
        for (String str : list) {
            if (str.length() == len) {
                list.remove(str);
                return;
            }
        }
        System.out.println(list);
    }

 

실제 결과는 GoGo가 삭제되지 못하는 것을 볼 수 있다.

    public static void main(String[] args) {
        init();
        forEachBaseDelete(4);
        System.out.println(list);
    }
    // [Hello, GoGo, World]

 

스트림 API 활용

하지만 자바 1.8에 추가된 스트림 API를 이용하면 좀 더 간단하게 삭제가 가능하다. 아래와 같이 removeIf 함수에 Predicate 형태의 자료를 전달해서 처리할 수 있다.

    static void forEachUsingStream(int len) {
        list.removeIf(item -> {
            return item.length() == len;
        });
        System.out.println(list);
    }

 

실행 결과는 예상대로 잘 출력됨을 알 수 있다.

    public static void main(String[] args) {
        init();
        forEachUsingStream(4);
    }
    //  [Hello, World]

'자바(SE)' 카테고리의 다른 글

밑이 2인 로그 구하기  (0) 2020.10.16
Comparator의 default method chaining  (0) 2020.08.13
Integer 사용 시 주의점  (0) 2020.08.07
[자료구조]Queue - add vs offer  (0) 2019.12.22
자바 trim() 메서드 버그?  (0) 2019.08.13
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.