【Java 集合】Collections 空列表细节处理
问题
如下代码,虽然定义为非空 NonNull,但依然会返回空对象,导致调用侧被检测为空引用。
实际上不是Collections的问题是三目运算符返回了null对象。
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
@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。
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<String> 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
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);
}
}