Stream을 List로 변환하는 2가지의 방법(Collectors.toList(), Stream.toList())
개요
사이드 프로젝트에서, 작성한 글에 대한 이미지 업로드 로직에서 이슈가 터지게 되었다.
사진 업로드 기능은 이미지를 업로드 창에 올리거나, 기존의 것을 삭제하거나, 업로드 창에 올린 것을 다시 삭제하는 등
다양한 경우의 수가 있어서 이를 잘 고려해서 설계해야한다.
우리는 2개의 배열을 두어 (업로드 할 이미지, 삭제 될 이미지) 서버로 전달받도록 하였고, 삭제할 이미지는
해당 포스트의 기존에 존재하던 이미지인 경우만 삭제하도록 구현하였다.
문제점
문제는 해당 메서드에서 발생했다.
해당 메서드는 List를 받아 element 1개를 제외한 List를 만들어 반환 해 주는 메서드인데
기존에는 주석처리 된 부분으로 코드 로직을 진행하였다.
하지만 java16 부터 등장한 toList() 메서드는 해당 메서드의 리턴 리스트 자료형이 unmodifiable하다고 한다.
따라서, 이 후 해당 리스트에 add 연산을 하고자 하면 해당 예외가 발생했다.
java.lang.UnsupportedOperationException: null
at java.util.AbstractList.add(AbstractList.java:148) ~[na:1.8.0_201]
at java.util.AbstractList.add(AbstractList.java:108) ~[na:1.8.0_201]
이유는, 우리가 toList() 메서드로 받는 arrayList 클래스는 AbstractList<E> 추상 클래스를 상속받는데,
해당 arrayList 클래스가 add 메서드를 override하지 않았기 때문에 추상클래스의 add 메서드가 그대로 호출이 되어
예외를 던지게 된 것이었다.
결과적으로, Collectors.toList() 메서드를 통해 modifiable한 arrayList를 사용하였고, 정상적으로 로직이 작동하였다.
하지만, Java 진영에서 Stream.toList() 를 권고하는 이유
람다와 스트림을 도입하게 된 java 8 버전부터 stream연산의 결과를 취합하여 리스트로 반환하는 종단연산인 .collect(Collectors.toList()) 를 사용할 수 있었다.
하지만 해당 메서드는 리턴되는 List가 수정이 가능(modifiable) 하다는 특징이 있어서, 메서드의 불변성을 지키기 위해 java10 에서 수정불가능한(unmodifiable) List 로 반환되도록 toUnmodifiableList() 가 새롭게 등장했다고 한다. 하지만 toUnmodfiableList() 는 이름이 장황해서, java16 에서는 이를 보완하기 위해 Stream.toList() 가 등장했다고 한다.
결과적으로 Collectors.toList() 메서드를 사용하긴 했지만, 해당 메서드를 사용하지 않는 방향으로 코드를 리팩토링 해 봐야 겠다.