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也是只读内部类。它们可以在多线程环境下使用,或者在需要保护列表免受修改的场景下使用。

相关推荐
荣码9 分钟前
长上下文与大文档处理:200页PDF怎么喂给AI,我踩了4个坑
java·python
樱花树下的数学喵9 分钟前
手写简易 STL string|C++ 字符串底层实现
开发语言·c++
极限挑战哥39 分钟前
共享电单车联营平台分账架构实践:二清合规与自动分账零人工方案
java·大数据·人工智能·微信小程序·架构·微信公众平台
jimy141 分钟前
C++ 报错的 qualifiers 的意思
开发语言·c++
皮皮林5511 小时前
比 VMware 更快,更轻的虚拟机,横空出世!
java
Thomas.Sir1 小时前
第13课:MyBatis-Plus整合SpringBoot4.x,分页、条件查询、逻辑删除实战
java·spring boot
meiying88382 小时前
多快递公司快递单号批量查询接口怎么对接?Python 聚合查询实现
java·大数据·python
古法安卓2 小时前
Android-GPU压测工具
android·java·android studio
不积跬步无以至千里mmbb262 小时前
rust_analysis
开发语言·后端·rust
SL_staff2 小时前
10人团队如何用轻量私有化文档平台实现知识复用提效?技术视角拆解B/S架构下的协作闭环
java·开源·全栈