본문 바로가기

자바(Java)

removeIf

반복문을 사용하지 않고 removeIf 를사용하면 코드를 얼마나 줄일 수 있는지 알아보자.

리스트가 주어졌을 때 리스트의 원소를 삭제하기 위해 아래와 같은 방법으로 작성하는 경우가 많다.

아래 예시는 referenceCode의 맨 앞자리가 정수인 객체를 삭제한다.

 

class Transaction {
    String referenceCode;

    public Transaction(String referenceCode) {
        this.referenceCode = referenceCode;
    }

    public String getReferenceCode() {
        return referenceCode;
    }

    @Override
    public String toString() {
        return referenceCode;
    }
}

반복을 돌 class 생성

 

 

List<Transaction> list = new ArrayList<>();
list.add(new Transaction("a1"));
list.add(new Transaction("1a"));
list.add(new Transaction("a2"));
list.add(new Transaction("2a"));

// ConcurrentModificationException
for(Transaction transaction: list) {
	if(Character.isDigit(transaction.getReferenceCode().charAt(0))) {
		list.remove(transaction);
	}
}

하지만 이렇게 작성하면 ConcurrentModificationException 라는 에러를 마주하게 된다. 반복문을 돌면서 원소를 삭제하면 반복문을 도는 객체의 길이가 바뀌게 된다. 반복자의 상태와 컬렉션의 상태가 서로 동기화 되지 않는 것이다. 자바에서는 이런 문제를 아예 에러로 처리해서 보여준다.

 

forEach는 내부적으로 iterator 객체를 사용하기 때문에 다음과 같이 고칠 수 있다.

for(Iterator<Transaction> iterator = list.iterator(); iterator.hasNext(); ) {
    Transaction transaction = iterator.next();
    if(Character.isDigit(transaction.getReferenceCode().charAt(0))) {
        iterator.remove();
    }
}

Iterator 객체를 명시적으로 사용하고, 그 객체의 remove 메서드를 호출하여 동기화 시켜 이 문제를 해결할 수 있다. 하지만 코드가 꽤 길어진 것을 볼 수 있다.

 

removeIf를 사용하면 코드를 훨씬 간단하게 만들 수 있다. removeIf 메서드는 삭제할 요소를 가리키는 프리디케이트를 인수로 받는다.

list.removeIf(transaction -> Character.isDigit(transaction.getReferenceCode().charAt(0)));

엄청나게 간단해진것을 볼 수있다.

 

default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}

removeIf는 실제로 위와같이 구현되어 있다. 위에서 iterator를 사용해 작성한것과 크게 다르지 않지만, 비즈니스 로직의 코드 길이를 줄일 수 있다는 것에 의의를 두자. 자주 사용된다면 코드의 중복을 상당히 줄일수 있을것이다.

 

참고

https://codechacha.com/ko/java-concurrentmodificationexception/