java.lang.UnsupportedOperationException分析及解决

今天在写业务的时候,需要对从数据库返回的List集合根据对象属性进行排序,那么常规的做法就是使用Collections的sort方法,实现Compartor接口重写compare方法,或者直接使用该list的sort方法,但是无论使用那种方法都遇到了这样的报错原因:

java 复制代码
java.lang.UnsupportedOperationException: null
	at java.util.Collections$UnmodifiableList.sort(Collections.java:1333) ~[na:1.8.0_331]
	at java.util.Collections.sort(Collections.java:177) ~[na:1.8.0_331]

无法支持的操作,再看报错第二行UnmodifiableList,这是一个不可变的集合,它继承 UnmodifiableCollection类,UnmodifiableCollection 中涉及到元素改动(新增、删除、清空...)的方法都直接抛出 UnsupportedOperationException 异常,并不改动元素;Iterator 中涉及到元素修改的方法也一样不进行元素的改动。

那么是不是把这个集合变成可变的集合问题是不就解决了呢?

java 复制代码
list = new ArrayList<>(list);

在排序前给这个list再次封装,问题得以解决。

java 复制代码
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                                  implements List<E> {
        private static final long serialVersionUID = -283967356065247728L;

        final List<? extends E> list;

        UnmodifiableList(List<? extends E> list) {
            super(list);
            this.list = list;
        }

        public boolean equals(Object o) {return o == this || list.equals(o);}
        public int hashCode()           {return list.hashCode();}

        public E get(int index) {return list.get(index);}
        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 int indexOf(Object o)            {return list.indexOf(o);}
        public int lastIndexOf(Object o)        {return list.lastIndexOf(o);}
        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()   {return listIterator(0);}

        public ListIterator<E> listIterator(final int index) {
            return new ListIterator<E>() {
                private final ListIterator<? extends E> i
                    = list.listIterator(index);

                public boolean hasNext()     {return i.hasNext();}
                public E next()              {return i.next();}
                public boolean hasPrevious() {return i.hasPrevious();}
                public E previous()          {return i.previous();}
                public int nextIndex()       {return i.nextIndex();}
                public int previousIndex()   {return i.previousIndex();}

                public void remove() {
                    throw new UnsupportedOperationException();
                }
                public void set(E e) {
                    throw new UnsupportedOperationException();
                }
                public void add(E e) {
                    throw new UnsupportedOperationException();
                }

                @Override
                public void forEachRemaining(Consumer<? super E> action) {
                    i.forEachRemaining(action);
                }
            };
        }

        public List<E> subList(int fromIndex, int toIndex) {
            return new UnmodifiableList<>(list.subList(fromIndex, toIndex));
        }

        /**
         * UnmodifiableRandomAccessList instances are serialized as
         * UnmodifiableList instances to allow them to be deserialized
         * in pre-1.4 JREs (which do not have UnmodifiableRandomAccessList).
         * This method inverts the transformation.  As a beneficial
         * side-effect, it also grafts the RandomAccess marker onto
         * UnmodifiableList instances that were serialized in pre-1.4 JREs.
         *
         * Note: Unfortunately, UnmodifiableRandomAccessList instances
         * serialized in 1.4.1 and deserialized in 1.4 will become
         * UnmodifiableList instances, as this method was missing in 1.4.
         */
        private Object readResolve() {
            return (list instanceof RandomAccess
                    ? new UnmodifiableRandomAccessList<>(list)
                    : this);
        }
    }

这里从UnmodifiableList的源码来分析:除了equals、hashCode、get、indexOf、lastIndexOf等方法外,其他方法都会抛出UnsupportedOperationException()异常,而这些方法的共同点都是返回该list的属性,说明UnmodifiableList是一个只读的集合,因此对这个List不能进行添加或删除元素等操作。

如果需要将list转化为不可变的集合,Collections也提供了这样的方法,最终转化为UnmodifiableList。

java 复制代码
  public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
    }

同样的 Collections 集合类中的 unmodifiableMap、unmodifiableSet也是只读内部类。它们可以在多线程环境下使用,或者在需要保护列表免受修改的场景下使用。

相关推荐
寻星探路2 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
lly2024063 小时前
Bootstrap 警告框
开发语言
2601_949146534 小时前
C语言语音通知接口接入教程:如何使用C语言直接调用语音预警API
c语言·开发语言
曹牧4 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
KYGALYX4 小时前
服务异步通信
开发语言·后端·微服务·ruby
zmzb01034 小时前
C++课后习题训练记录Day98
开发语言·c++
爬山算法5 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7255 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎5 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄5 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea