【Java 集合】Collections 空列表细节处理

问题

如下代码,虽然定义为非空 NonNull,但依然会返回空对象,导致调用侧被检测为空引用。

实际上不是Collections的问题是三目运算符返回了null对象。

java 复制代码
import java.util.Collections;

    @NonNull
    private List<String> getInfo() {
        IccRecords iccRecords = mPhone.getIccRecords();
        
        if(iccRecords != null) {
            //省略逻辑
            String[] simSpdi = iccRecords.getServiceProviderDisplayInformation();
            return simSpdi != null ? Arrays.asList(simSpdi) : null; //根因是这里返回null
        }
        return Collections.EMPTY_LIST;
    }

源码案例

frameworks/opt/telephony/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java

java 复制代码
    @NonNull
    private List<String> getEfSpdi() {
        for (int i = 0; i < mEf.size(); i++) {
            if (mEf.valueAt(i).getServiceProviderDisplayInformation() != null) {
                return mEf.valueAt(i).getServiceProviderDisplayInformation();
            }
        }
        return Collections.EMPTY_LIST;
    }

    @NonNull
    private String getEfSpn() {
        for (int i = 0; i < mEf.size(); i++) {
            if (!TextUtils.isEmpty(mEf.valueAt(i).getServiceProviderName())) {
                return mEf.valueAt(i).getServiceProviderName();
            }
        }
        return "";
    }

    @NonNull
    private List<OperatorPlmnInfo> getEfOpl() {
        for (int i = 0; i < mEf.size(); i++) {
            if (mEf.valueAt(i).getOperatorPlmnList() != null) {
                return mEf.valueAt(i).getOperatorPlmnList();
            }
        }
        return Collections.EMPTY_LIST;
    }

    @NonNull
    private List<PlmnNetworkName> getEfPnn() {
        for (int i = 0; i < mEf.size(); i++) {
            if (mEf.valueAt(i).getPlmnNetworkNameList() != null) {
                return mEf.valueAt(i).getPlmnNetworkNameList();
            }
        }
        return Collections.EMPTY_LIST;
    }

代码解析

注意Collection和Collections是不同的。

  • libcore/ojluni/src/main/java/java/util/Collections.java 工具类
  • libcore/ojluni/src/main/java/java/util/Collection.java 接口

Collections中定义了内部类EmptyList,在静态List常量EMPTY_LIST(immutable不可变列表)初始化时会new一个没有指定类型的EmptyList。

java 复制代码
public class Collections {
    /**
     * The empty list (immutable).  This list is serializable.
     *
     * @see #emptyList()
     */
    @SuppressWarnings("rawtypes")
    public static final List EMPTY_LIST = new EmptyList<>();

    /**
     * Returns an empty list (immutable).  This list is serializable.
     *
     * <p>This example illustrates the type-safe way to obtain an empty list:
     * <pre>
     *     List&lt;String&gt; s = Collections.emptyList();
     * </pre>
     *
     * @implNote
     * Implementations of this method need not create a separate {@code List}
     * object for each call.   Using this method is likely to have comparable
     * cost to using the like-named field.  (Unlike this method, the field does
     * not provide type safety.)
     *
     * @param <T> type of elements, if there were any, in the list
     * @return an empty immutable list
     *
     * @see #EMPTY_LIST
     * @since 1.5
     */
    @SuppressWarnings("unchecked")
    public static final <T> List<T> emptyList() {
        return (List<T>) EMPTY_LIST;
    }

    /**
     * @serial include
     */
    private static class EmptyList<E>
        extends AbstractList<E>
        implements RandomAccess, Serializable {
        @java.io.Serial
        private static final long serialVersionUID = 8842843931221139166L;

        public Iterator<E> iterator() {
            return emptyIterator();
        }
        public ListIterator<E> listIterator() {
            return emptyListIterator();
        }

        // Preserves singleton property
        @java.io.Serial
        private Object readResolve() {
            return EMPTY_LIST;
        }
    }

}

解决方案

将 Collections.EMPTY_LIST 替换成 Collections.emptyList()。

虽然它们都可以用于表示一个空的不可变列表,但 Collections.emptyList() 是更优先的选择,因为它提供了类型安全性和更好的代码可读性。

附:Collections 源码

一些值得学习的语法,Android 扩展的排序跟Java 集合原生排序的实现差异

libcore/ojluni/src/main/java/java/util/Collections.java

java 复制代码
import dalvik.system.VMRuntime;

/**
 * This class consists exclusively of static methods that operate on or return
 * collections.  It contains polymorphic algorithms that operate on
 * collections, "wrappers", which return a new collection backed by a
 * specified collection, and a few other odds and ends.
 *
 * <p>The methods of this class all throw a {@code NullPointerException}
 * if the collections or class objects provided to them are null.
 *
 * <p>The documentation for the polymorphic algorithms contained in this class
 * generally includes a brief description of the <i>implementation</i>.  Such
 * descriptions should be regarded as <i>implementation notes</i>, rather than
 * parts of the <i>specification</i>.  Implementors should feel free to
 * substitute other algorithms, so long as the specification itself is adhered
 * to.  (For example, the algorithm used by {@code sort} does not have to be
 * a mergesort, but it does have to be <i>stable</i>.)
 *
 * <p>The "destructive" algorithms contained in this class, that is, the
 * algorithms that modify the collection on which they operate, are specified
 * to throw {@code UnsupportedOperationException} if the collection does not
 * support the appropriate mutation primitive(s), such as the {@code set}
 * method.  These algorithms may, but are not required to, throw this
 * exception if an invocation would have no effect on the collection.  For
 * example, invoking the {@code sort} method on an unmodifiable list that is
 * already sorted may or may not throw {@code UnsupportedOperationException}.
 *
 * <p>This class is a member of the
 * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
 * Java Collections Framework</a>.
 *
 * @author  Josh Bloch
 * @author  Neal Gafter
 * @see     Collection
 * @see     Set
 * @see     List
 * @see     Map
 * @since   1.2
 */

public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {
    }

    // Algorithms

    // Android-added: List.sort() vs. Collections.sort() app compat.
    // Added a warning in the documentation.
    // Collections.sort() calls List.sort() for apps targeting API version >= 26
    // (Android Oreo) but the other way around for app targeting <= 25 (Nougat).
    /**
     * Sorts the specified list into ascending order, according to the
     * {@linkplain Comparable natural ordering} of its elements.
     * All elements in the list must implement the {@link Comparable}
     * interface.  Furthermore, all elements in the list must be
     * <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)}
     * must not throw a {@code ClassCastException} for any elements
     * {@code e1} and {@code e2} in the list).
     *
     * <p>This sort is guaranteed to be <i>stable</i>:  equal elements will
     * not be reordered as a result of the sort.
     *
     * <p>The specified list must be modifiable, but need not be resizable.
     *
     * @implNote
     * This implementation defers to the {@link List#sort(Comparator)}
     * method using the specified list and a {@code null} comparator.
     * Do not call this method from {@code List.sort()} since that can lead
     * to infinite recursion. Apps targeting APIs {@code <= 25} observe
     * backwards compatibility behavior where this method was implemented
     * on top of {@link List#toArray()}, {@link ListIterator#next()} and
     * {@link ListIterator#set(Object)}.
     *
     * @param  <T> the class of the objects in the list
     * @param  list the list to be sorted.
     * @throws ClassCastException if the list contains elements that are not
     *         <i>mutually comparable</i> (for example, strings and integers).
     * @throws UnsupportedOperationException if the specified list's
     *         list-iterator does not support the {@code set} operation.
     * @throws IllegalArgumentException (optional) if the implementation
     *         detects that the natural ordering of the list elements is
     *         found to violate the {@link Comparable} contract
     * @see List#sort(Comparator)
     */
    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        // Android-changed: List.sort() vs. Collections.sort() app compat.
        // Call sort(list, null) here to be consistent with that method's
        // (changed on Android) behavior.
        // list.sort(null);
        sort(list, null);
    }


}
相关推荐
Chen-Edward9 小时前
有了Spring为什么还有要Spring Boot?
java·spring boot·spring
陈小桔10 小时前
idea中重新加载所有maven项目失败,但maven compile成功
java·maven
小学鸡!10 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
xiaogg367810 小时前
阿里云k8s1.33部署yaml和dockerfile配置文件
java·linux·kubernetes
逆光的July11 小时前
Hikari连接池
java
微风粼粼11 小时前
eclipse 导入javaweb项目,以及配置教程(傻瓜式教学)
java·ide·eclipse
番茄Salad11 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud
天若有情67311 小时前
Spring MVC文件上传与下载全面详解:从原理到实战
java·spring·mvc·springmvc·javaee·multipart
祈祷苍天赐我java之术11 小时前
Redis 数据类型与使用场景
java·开发语言·前端·redis·分布式·spring·bootstrap
Olrookie12 小时前
若依前后端分离版学习笔记(二十)——实现滑块验证码(vue3)
java·前端·笔记·后端·学习·vue·ruoyi