일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Kafka
- ls -lgaf
- 자바
- dto매핑우선순위
- 스트림
- group by group by rollup 차이
- 컬럼명중복
- sql 테스트 사이트
- 중복컬럼dto매핑
- 설치없이쿼리실행
- hibernate 쿼리실행 순서
- 쿼리테스트사이트
- 쿼리실행사이트
- Flush
- 쓰기지연저장소
- spring kakfa
- 쓰기지연sql저장소 쿼리실행순서
- spring cloud stream
- JPA
- 포트앤어댑터 아키텍처
- IntelliJ
- error 2002 (hy000): can't connect to local mysql server through socket '/tmp/mysql.sock' (2)
- sql사이트
- Stream
- 쿼리실행순서
- Java
- 쿼리사이트
- 오라클쿼리테스트사이트
- port&adapter architecture
- Oracle
- Today
- Total
개린이 탈출기
[JAVA] List 에 add가 왜 안돼? (feat.stream) 본문
결론부터 말하자면
스트림의 toList()를 사용하여 만든 컬렉션은 수정불가한 List를 반환하기 때문에 collect(Collectors.toList()) 로 코드를 수정하면 바로 해결 가능하다.
1. 문제 발생
기존에 작업해 놓았던 코드를 예쁘게 다듬던 도중 다음과 같은 예외가 발생했다.
java.lang.UnsupportedOperationException: null
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142) ~[na:na]
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.addAll(ImmutableCollections.java:148) ~[na:na]
해당 예외가 발생한 부분의 소스코드는 다음과 같았다.
List<String> cdList = new ArrayList<>();
for (myEntity entity : entityList) {
cdList.add(entityList.getCd());
}
cdList = userEmpnos.stream().filter(Objects::nonNull).distinct().toList();
////////////////// 중략 ///////////////
// 예외 발생 라인
cdList.addAll(nextEntityList.stream().map(MyEntity::getCd()).toList());
2. 문제 분석
자바 Stream 의 toList() 메서드는 다음과 같이 구현되어있다.
default List<T> toList() {
return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
}
해당 메서드 toList()는 Collections 의 unmodifiableList 메서드를 호출하여 반환한다.
딱 보기에도 수정할 수 없는 컬렉션을 반환하는 것 같은데, 그 속을 자세히 알아보면 다음과 같다.
public static <T> List<T> unmodifiableList(List<? extends T> list) {
if (list.getClass() == UnmodifiableList.class || list.getClass() == UnmodifiableRandomAccessList.class) {
return (List<T>) list;
}
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
여기서 UnmodifiableList 내부를 살펴보면 다음과 같다.
static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
@java.io.Serial
private static final long serialVersionUID = 1820017752578914078L;
@SuppressWarnings("serial") // Conditionally serializable
final Collection<? extends E> c;
UnmodifiableCollection(Collection<? extends E> c) {
if (c==null)
throw new NullPointerException();
this.c = c;
}
public int size() {return c.size();}
public boolean isEmpty() {return c.isEmpty();}
public boolean contains(Object o) {return c.contains(o);}
public Object[] toArray() {return c.toArray();}
public <T> T[] toArray(T[] a) {return c.toArray(a);}
public <T> T[] toArray(IntFunction<T[]> f) {return c.toArray(f);}
public String toString() {return c.toString();}
public Iterator<E> iterator() {
return new Iterator<>() {
private final Iterator<? extends E> i = c.iterator();
public boolean hasNext() {return i.hasNext();}
public E next() {return i.next();}
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
// Use backing collection version
i.forEachRemaining(action);
}
};
}
public boolean add(E e) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection<?> coll) {
return c.containsAll(coll);
}
public boolean addAll(Collection<? extends E> coll) {
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {
c.forEach(action);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException();
}
@SuppressWarnings("unchecked")
@Override
public Spliterator<E> spliterator() {
return (Spliterator<E>)c.spliterator();
}
@SuppressWarnings("unchecked")
@Override
public Stream<E> stream() {
return (Stream<E>)c.stream();
}
@SuppressWarnings("unchecked")
@Override
public Stream<E> parallelStream() {
return (Stream<E>)c.parallelStream();
}
}
내가 이해한 바를 가볍게 설명해보자면,
1. 클래스 생성 시 받은 컬렉션을 그대로 받아 사용한다.
2. 컬렉션 자체에 대한 변경이 일어나는 메서드는 모두 UnsupportedOperationException 예외를 발생시킨다.
내 코드에서 발생했던 에러가 바로 UnsupportedOperationException 예외였고,
예외의 원인은 UnmodifiableList 에 addAll() 을 시도했기 때문이었다.
3. 문제 해결
collect() 메서드를 이용하여 문제를 해결할 수 있었다.
Map 을 만들 때 사용하던 방식이었는데, List도 만들 수 있는 메서드였다.
collect() 메서드 파라미터로 Collector 를 전달해주면 되는데, toList() 메서드를 활용하면 간단히 컬렉션을 생성하여 반환할 수 있다.
이 메서드는 toList() 와 다르게 ArrayList 를 반환하기 때문에 수정이 가능하기 때문이다.
따라서 나는 다음과 같이 코드를 수정하여 문제를 해결했다.
List<String> cdList = new ArrayList<>();
for (myEntity entity : entityList) {
cdList.add(entityList.getCd());
}
// 수정 전 코드
// cdList = userEmpnos.stream().filter(Objects::nonNull).distinct().toList();
// 수정 후 코드
cdList = userEmpnos.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
////////////////// 중략 ///////////////
cdList.addAll(nextEntityList.stream().map(MyEntity::getCd()).toList());
'에러 해결 목록' 카테고리의 다른 글
[JAVA] Unable to make protected final java.lang.Class 예외 해결 (0) | 2024.11.06 |
---|---|
[JAVA] Gradle 구동 과정 간단 정리 (1) | 2024.11.05 |
[JPA] flush 와 clear 의 차이 (2) | 2024.09.26 |
[Java] stream 의 peek 과 map의 차이 (1) | 2024.09.13 |
[Oracle] (+) 유무에 따른 WHERE 절의 조건 해석 (1) | 2024.09.10 |