4 minute read

Stream.collect(Collectors.toList()) 와 Stream.toList() λŠ” λ‹€λ₯΄λ‹€!

image


μ–΄λŠλ‚  production 의 λ ˆκ±°μ‹œ λ‘œμ§μ—μ„œ λ°œμƒν•œ UnsupportedOperationException…

무엇이 λ¬Έμ œμ˜€μ„κΉŒμš”?


java 둜 λ°±μ—”λ“œ κ°œλ°œμ„ ν•˜λ‹€ 보면,

κ°€μž₯ ν”ν•œ νŒ¨ν„΄ 쀑 ν•˜λ‚˜κ°€

νŠΉμ • 데이터λ₯Ό μ›ν•˜λŠ” ν˜•νƒœλ‘œ λ³€ν™˜ν•˜κ±°λ‚˜ μΆ”μΆœν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

μ΄λ•ŒλŠ” Stream API λ₯Ό μ‚¬μš©ν•˜λ©΄ νŽΈλ¦¬ν•˜κ³ , 일반적으둜 쒋은 가독성을 μœ μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ•„λž˜ μ½”λ“œλ₯Ό μ‚΄νŽ΄λ΄…μ‹œλ‹€.

        List<String> imageUrls = articleImageRepository.findByArticle(article)
                .stream()
                .map(ArticleImage::getImageUrl)
                .collect(Collectors.toList());

μœ„ μ½”λ“œ μŠ€λ‹ˆνŽ«μ—μ„œλŠ” articleImage μ—”ν‹°ν‹° 리슀트λ₯Ό μΏΌλ¦¬ν•΄μ˜¨ 뒀에, ImageUrl 만 List 으둜 μΆ”μΆœν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

μ—¬κΈ°μ„œ μ£Όλͺ©ν•΄μ•Ό ν•  뢀뢄은 collect(Collectors.toList() μž…λ‹ˆλ‹€.

ν•΄λ‹Ή 뢀뢄에 μ»€μ„œλ₯Ό κ°€μ Έλ‹€ λŒ€λ©΄ intelliJ μ—μ„œλŠ” μ•„λž˜μ™€ 같은 메세지λ₯Ό λ„μ›Œμ€λ‹ˆλ‹€.

`collect(toList())` can be replaced with 'toList()'

- Replace 'collect(toList())' with 'toList()'?

이 μ œμ•ˆμ„ 무심코 μˆ˜λ½ν•˜λ‹€κ°€λŠ”, μ˜ˆμƒμΉ˜ λͺ»ν•œ 잠재적인 버그λ₯Ό μ„œλΉ„μŠ€μ— 심을 수 μžˆμŠ΅λ‹ˆλ‹€.

 πŸ”₯ Stream.collect(Collectors.toList()) 와 Stream.toList() λŠ” λͺ…λ°±ν•˜κ²Œ λ‹€λ₯΄λ‹€! 

μ–΄λ–»κ²Œ λ‹€λ₯ΌκΉŒ?

μ£Όμš”ν•œ 차이점은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.


- Stream.collect(Collectors.toList()) λŠ” κ°€λ³€ 리슀트λ₯Ό λ°˜ν™˜ν•œλ‹€.

- Stream.toList() λŠ” λΆˆλ³€ 리슀트λ₯Ό λ°˜ν™˜ν•œλ‹€.

jdk 16 λΆ€ν„° λ„μž…λœ λ©”μ„œλ“œ 인 Stream.toList() λŠ” λΆˆλ³€ 리슀트λ₯Ό λ°˜ν™˜ν•˜λ©°, μ™ΈλΆ€μ—μ„œ μˆ˜μ • μ‹œλ„ μ‹œ UnsupportedOperationException 이 ν„°μ§‘λ‹ˆλ‹€.


List<String> mutableList = Stream.of("a", "b").collect(Collectors.toList());
mutableList.add("c"); // βœ… κ°€λŠ₯

List<String> immutableList = Stream.of("a", "b").toList();
immutableList.add("c"); // πŸ”₯ UnsupportedOperationException λ°œμƒ

ν•„μš”ν•œ κ΅¬ν˜„μ²΄κ°€ λΆˆλ³€ λ¦¬μŠ€νŠΈμΈμ§€, κ°€λ³€λ¦¬μŠ€νŠΈμΈμ§€ μš”κ΅¬μ‚¬ν•­μ„ μ •ν™•ν•˜κ²Œ νŒŒμ•…ν•˜κ³  μ‚¬μš©ν•˜λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€.

IntelliJ, SonarQube λ“±μ—μ„œ collect(Collectors.toList()) λ₯Ό Stream.toList() 으둜 λ³€κ²½ν•˜λŠ” 것을 κΆŒν•  수 μžˆμ§€λ§Œ,

두 λ©”μ„œλ“œμ˜ κΈ°λŠ₯적인 차이점을 λͺ…ν™•νžˆ μΈμ§€ν•˜μ§€ λͺ»ν•œ 채 받아듀이면

상황에 따라 κ½€λ‚˜ μ‹¬κ°ν•œ 버그가 λ°œμƒν•  μˆ˜λ„ 있겠죠.

λŒλ‹€λ¦¬λ„ 두듀겨 보고 κ±΄λ„ˆμ•Ό ν•  것 κ°™μŠ΅λ‹ˆλ‹€.


λ‚΄λΆ€ κ΅¬ν˜„ μ½”λ“œλ₯Ό λ”°λΌκ°€μ„œ 두 눈으둜 κ²€μ¦ν•˜κΈ°

μ‹œκ°„μ μœΌλ‘œ ν—ˆμš©λœλ‹€λ©΄ λ°˜λ“œμ‹œ λ‚΄λΆ€ κ΅¬ν˜„ μ½”λ“œλ₯Ό ν™•μΈν•΄μ„œ, 슀슀둜 λ‚©λ“ν•˜λŠ” 것이 κ°€μž₯ 기얡에 였래 λ‚¨λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€.

jdk 17 κΈ°μ€€μœΌλ‘œ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

λ¨Όμ € Stream.collect(Collectors.toList()) λΆ€ν„° λ”°λΌκ°€λ΄…μ‹œλ‹€.


    /**
     * Accumulates the elements of this stream into a {@code List}. The elements in
     * the list will be in this stream's encounter order, if one exists. The returned List
     * is unmodifiable; calls to any mutator method will always cause
     * {@code UnsupportedOperationException} to be thrown. There are no
     * guarantees on the implementation type or serializability of the returned List.
     *
     * <p>The returned instance may be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
     * Callers should make no assumptions about the identity of the returned instances.
     * Identity-sensitive operations on these instances (reference equality ({@code ==}),
     * identity hash code, and synchronization) are unreliable and should be avoided.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
     *
     * @apiNote If more control over the returned object is required, use
     * {@link Collectors#toCollection(Supplier)}.
     *
     * @implSpec The implementation in this interface returns a List produced as if by the following:
     * <pre>{@code
     * Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())))
     * }</pre>
     *
     * @implNote Most instances of Stream will override this method and provide an implementation
     * that is highly optimized compared to the implementation in this interface.
     *
     * @return a List containing the stream elements
     *
     * @since 16
     */
    @SuppressWarnings("unchecked")
    default List<T> toList() {
        return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
    }


    /**
     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
     * specified list. Query operations on the returned list "read through" to the
     * specified list, and attempts to modify the returned list, whether
     * direct or via its iterator, result in an
     * {@code UnsupportedOperationException}.<p>
     *
     * The returned list will be serializable if the specified list
     * is serializable. Similarly, the returned list will implement
     * {@link RandomAccess} if the specified list does.
     *
     * @implNote This method may return its argument if the argument is already unmodifiable.
     * @param  <T> the class of the objects in the list
     * @param  list the list for which an unmodifiable view is to be returned.
     * @return an unmodifiable view of the specified list.
     */
    @SuppressWarnings("unchecked")
    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));
    }

μ΄λ²ˆμ—λŠ” collect(Collectors.toList()) λ₯Ό λ΄…μ‹œλ‹€.


    /**
     * Returns a {@code Collector} that accumulates the input elements into a
     * new {@code List}. There are no guarantees on the type, mutability,
     * serializability, or thread-safety of the {@code List} returned; if more
     * control over the returned {@code List} is required, use {@link #toCollection(Supplier)}.
     *
     * @param <T> the type of the input elements
     * @return a {@code Collector} which collects all the input elements into a
     * {@code List}, in encounter order
     */
    public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>(ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }


무엇이 λ‹€λ₯Έκ°€μš”?

toList() μ—μ„œ λ°˜ν™˜ν•˜λŠ” κ΅¬ν˜„μ²΄λŠ” Collectors.UnmodifiableRandomAccessList, Collectors.UnmodifiableList 으둜 λΆˆλ³€ λ¦¬μŠ€νŠΈμ΄μ§€λ§Œ

collect(Collectors.toList()) μ—μ„œ λ°˜ν™˜ν•˜λŠ” κ΅¬ν˜„μ²΄λŠ” ArrayList 으둜 κ°€λ³€ λ¦¬μŠ€νŠΈμž…λ‹ˆλ‹€.


λΆˆλ³€ 리슀트의 μ›μ†Œλ₯Ό μˆ˜μ •ν•˜λ €κ³  ν•˜λ©΄ UnsupportedOperationException 이 λ°œμƒν•˜κ³ ,

ν•΄λ‹Ή μ˜ˆμ™ΈλŠ” runtime 에, ν•΄λ‹Ή line 이 μˆ˜ν–‰λ  λ•Œμ—λ§Œ λ°œμƒν•˜λ―€λ‘œ μ°ΎκΈ° μ–΄λ ΅μ£ .

UnsupportedOperationException 이 triggering λ˜λŠ” μ½”λ“œλŠ” UnmodifiableList λ‚΄λΆ€ μ½”λ“œλ₯Ό ν™•μΈν•΄μ„œ 확신을 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.


public static <T> List<T> unmodifiableList(List<? extends T> list) {
         // ... μƒλž΅ ...
    }

    /**
     * @serial include
     */
    static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                                  implements List<E> {
        // ... μƒλž΅ ...
        public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }
        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
        public E remove(int index) {
            throw new UnsupportedOperationException();
        }
        public boolean addAll(int index, Collection<? extends E> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            throw new UnsupportedOperationException();
        }
        @Override
        public void sort(Comparator<? super E> c) {
            throw new UnsupportedOperationException();
        }

        // ... μƒλž΅ ...

        public ListIterator<E> listIterator(final int index) {
            return new ListIterator<E>() {
                
                // ... μƒλž΅ ...

                public void remove() {
                    throw new UnsupportedOperationException();
                }
                public void set(E e) {
                    throw new UnsupportedOperationException();
                }
                public void add(E e) {
                    throw new UnsupportedOperationException();
                }
            };
          // ... μƒλž΅ ...
        }

P.S.

항상 μ˜μ‹¬ν•΄...

References

Categories:

Updated:

Leave a comment