GVKun编程网logo

Collections.emptyList()而不是null检查?(null不是java的关键字)

9

本篇文章给大家谈谈Collections.emptyList,以及而不是null检查?的知识点,同时本文还将给你拓展android-kotlin.TypeCastException:无法将null强制

本篇文章给大家谈谈Collections.emptyList,以及而不是null检查?的知识点,同时本文还将给你拓展android-kotlin.TypeCastException:无法将null强制转换为非null类型的kotlin.collections.Map、Arrays.asList()vs Collections.singletonList()、Arrays.asList()与Collections.singletonList()、Collections(二)List上篇-ArrayList等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

Collections.emptyList()而不是null检查?(null不是java的关键字)

Collections.emptyList()而不是null检查?(null不是java的关键字)

如果我在某个类中有一个很少使用的集合,该集合可能被实例化很多次,则有时我可能会求助于以下“习惯用法”以节省不必要的对象创建:

List<Object> list = null;void add(Object object) {    if (list == null)        list = new ArrayList<Object>();    list.add(object);}// somewhere elseif (list != null)    for (Object object : list)         ;

现在,我想知道是否无法使用来消除这些空检查Collections.emptyList(),但是我将不得不add()像这样更改if检查:

if (list == Collections.<Object>emptyList())    list = new ArrayList<Object>();

除了每次都分配一个新的空集合以外,还有更好的方法来处理此问题吗?

编辑: 为了清楚起见,我
使用Collections.emptyList(),但是在add()中进行上述检查确实非常丑陋……我想知道是否有更好的方法甚至是其他方法处理这个。

答案1

小编典典

为了节省不必要的对象创建

这是一个非常糟糕的主意,它将使== null检查和其他处理极端情况的代码变得乱七八糟(无论如何最终都会导致空指针异常)!

现在我想知道是否无法使用Collections.emptyList()

不,不是。emptyList()返回一个空列表。你 可以

if (list.equals(Collections.<Object>emptyList()))

但是,如果list == null,它仍然会抛出NullPointerException ,所以它仍然不是您想要的。

我的建议:始终将列表初始化为newArrayList<Object>,或者,例如,如果要从方法返回空列表,请Collections.emptyList()改用。(这每次都会返回相同的实例,因此也不会在其中创建不必要的对象。)

然后使用.isEmpty()来检查集合是否为空。

android-kotlin.TypeCastException:无法将null强制转换为非null类型的kotlin.collections.Map

android-kotlin.TypeCastException:无法将null强制转换为非null类型的kotlin.collections.Map

我一直在寻找可以广泛帮助我的代码的答案,但是我发现的解决方案对我不起作用.

我收到以下错误:

    kotlin.TypeCastException: null cannot be cast to non-null 
    type kotlin.collections.Map<kotlin.String, kotlin.Any>

    FATAL EXCEPTION: main
    Process: com.meetHitch.HitchApp, PID: 4021
    kotlin.TypeCastException: null cannot be cast to non-null 
    type kotlin.collections.Map<kotlin.String, kotlin.Any>
      at ... helpers.RestAPIKt$getUserProfile$1 ...
      at com.google.android.gms.tasks.zzj.run(UnkNown Source)
      at android.os.Handler.handleCallback(Handler.java:751)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:154)
      at android.app.ActivityThread.main ...
      at java.lang.reflect.Method.invoke(Native Method)
      at com.android.internal.os.ZygoteInit$Method...
      at com.android.internal.os.ZygoteInit.main

我的代码如下:

private val TAG: String
get() = "RestAPI"

private val fUser: FirebaseUser?
get() = FirebaseAuth.getInstance().currentUser

private val uid: String?
get() = fUser?.uid

private val baseDoc: DocumentReference
get() = FirebaseFirestore.getInstance().
        collection("users").document(uid!!)

private val leadsDoc: DocumentReference
get() {
    appUser.currentConference?.id?.let {
        return baseDoc.collection("leads").document(it)
    }
    return baseDoc.collection("leads").document(demoID)
}

private val conferencesCollection: CollectionReference
get() =  FirebaseFirestore.getInstance().
         collection("conferences")

fun getUserProfile(callback: (AppUser) -> Unit) {

  // Source can be CACHE, SERVER, or DEFAULT.
  val source = Source.DEFAULT

  baseDoc.get(source)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val document = task.result
                if (document != null) {
                    printnLog(TAG, "Loaded profile for User 
                    with UID: $uid successfully!")

                    val user = AppUser(task.result.data as 
                               Map<String, Any>)
                    callback(user)
                } else {
                    println("No profile set. Saving initial 
                             profile...")
                    appUser = AppUser(fUser!!)
                    setUserProfile()
                }
            } else {
                Log.w(TAG, "getUser:onCancelled", 
                      task.exception)
            }
          }
        }

我指的是kotlin.TypeCastException: null cannot be cast to non-null type com.midsizemango.databasekotlin.Note,尽管有可能我没有实现,但所有解决方案都没有起作用?在正确的地方.我尝试了“ as?Map”,但出现类型不匹配错误.

解决方法:

使用val user = AppUser(task.result.data as Map< String,Any>)行,使Any变量可为空:

val user = AppUser(task.result.data as Map<String, Any?>)

Arrays.asList()vs Collections.singletonList()

Arrays.asList()vs Collections.singletonList()

Collections.singletonList(something)不可变的,

Collections.singletonList(something)返回的列表所做的任何更改将导致UnsupportedOperationException 。

Arrays.asList(something)允许Arrays.asList(something) 更改  。

 

此外,由Collections.singletonList(something)返回的List的容量将始终为1,

        而Arrays.asList(something)的容量将为已支持数组的大小。

 /**


* Returns an immutable list containing only the specified object. * The returned list is serializable. * *
@param <T> the class of the objects in the list * @param o the sole object to be stored in the returned list. * @return an immutable list containing only the specified object. * @since 1.3 */ public static <T> List<T> singletonList(T o) { return new SingletonList<>(o); } /** * @serial include */ private static class SingletonList<E> extends AbstractList<E> implements RandomAccess, Serializable { private static final long serialVersionUID = 3093736618740652951L; private final E element; SingletonList(E obj) {element = obj;} public Iterator<E> iterator() { return singletonIterator(element); } public int size() {return 1;} public boolean contains(Object obj) {return eq(obj, element);} public E get(int index) { if (index != 0) throw new IndexOutOfBoundsException("Index: "+index+", Size: 1"); return element; } // Override default methods for Collection @Override public void forEach(Consumer<? super E> action) { action.accept(element); } @Override public boolean removeIf(Predicate<? super E> filter) { throw new UnsupportedOperationException(); } @Override public void replaceAll(UnaryOperator<E> operator) { throw new UnsupportedOperationException(); } @Override public void sort(Comparator<? super E> c) { } @Override public Spliterator<E> spliterator() { return singletonSpliterator(element); } }

 

 

 

 

附录:
package com.ysyc.invoicecertify.util.mockservice;

import java.util.Arrays;
import java.util.List;

/**
 *
 * 本类演示了Arrays类中的asList方法
 * 通过四个段落来演示,体现出了该方法的相关特性.
 *
 * (1) 该方法对于基本数据类型的数组支持并不好,当数组是基本数据类型时不建议使用
 * (2) 当使用asList()方法时,数组就和列表链接在一起了.
 *     当更新其中之一时,另一个将自动获得更新。
 *     注意:仅仅针对对象数组类型,基本数据类型数组不具备该特性
 * (3) asList得到的数组是的没有add和remove方法的
 *
 * 通过查看Arrays类的源码可以知道,asList返回的List是Array中的实现的
 * 内部类,而该类并没有定义add和remove方法.另外,为什么修改其中一个,另一个也自动
 * 获得更新了,因为asList获得List实际引用的就是数组
 */
public class AsListTest {

    public static void main(String[] args) {

        /* 段落一:基本数据类型使用asList中的问题 */

        /* 说明:虽然在JDK1.6中能够将基本数据类型的数组转换成List,但还是有个缺陷 */
        int[] a_int = { 1, 2, 3, 4 };
        /* 预期输出应该是1,2,3,4,但实际上输出的仅仅是一个引用, 这里它把a_int当成了一个元素 */
        List a_int_List = Arrays.asList(a_int);
        foreach(a_int_List);
        /* 为此我们需要这样遍历其中元素 */
        foreachForBase(a_int_List);
        System.out.println("1 END 2 START");
        /* 段落二:对象类型的数组使用asList,是我们预期的 */
        Integer[] a_Integer = new Integer[] { 1, 2, 3, 4 };
        List a_Integer_List = Arrays.asList(a_Integer);
        foreach(a_Integer_List);
        System.out.println("2 END 3 START");

        /* 段落三:当更新数组或者asList之后的List,另一个将自动获得更新 */
        a_Integer_List.set(0, 0);
        foreach(a_Integer_List);
        foreach(a_Integer);
        System.out.println("3 END 4 START");

        a_Integer[0] = 5;
        foreach(a_Integer_List);
        foreach(a_Integer);

        /* 段落四:对基本类型数组,通过asList之后的List修改对应的值后,在运行时会报出异常
         * 但是基本类型数组对应的List是会发生变化的,这是毫无疑问的
         */

         a_int_List.set(0, 0);
          foreach(a_int_List);
          foreach(a_int);

        System.out.println("4 END 5 START");
        a_int[0] = 5;
        foreachForBase(a_int_List);
        foreach(a_int);

    }

    /* 打印方法 */
    private static void foreach(List list) {
        for (Object object : list) {
            System.out.print(object + " ");
        }
        System.out.println();

    }

    private static void foreachForBase(List a_int_List) {
        int[] _a_int = (int[]) a_int_List.get(0);
        foreach(_a_int);
    }

    private static void foreach(int[] a_int) {
        for (int i : a_int) {
            System.out.print(i + " ");
        }
        System.out.println();
    }

    private static void foreach(Integer[] _a_Integer) {
        for (int i : _a_Integer) {
            System.out.print(i + " ");
        }
        System.out.println();
    }
}

console:

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
    at java.util.Arrays$ArrayList.set(Arrays.java:3847)
    at com.ysyc.invoicecertify.util.mockservice.AsListTest.main(AsListTest.java:56)
[I@762efe5d 
1 2 3 4 
1 END 2 START
1 2 3 4 
2 END 3 START
0 2 3 4 
0 2 3 4 
3 END 4 START
5 2 3 4 
5 2 3 4 
Disconnected from the target VM, address: ''127.0.0.1:54490'', transport: ''socket''

Process finished with exit code 1

 

 

Arrays.asList()与Collections.singletonList()

Arrays.asList()与Collections.singletonList()

使用Arrays.asList(something)优于Collections.singletonList(something)来构成包含一项的列表是否有优势(或有很大不同)?后者使返回的列表也不可变。

答案1

小编典典

Collections.singletonList(something)不可变的,
而数组Arrays.asList(something)是固定大小的List表示形式,列表和数组在堆中连接在一起。

Arrays.asList(something)允许对其进行 非结构更改 ,这些 更改 将同时反映到List和联合数组中。
UnsupportedOperationException 尽管您可以为特定索引设置元素,但它会添加,删除元素。

对返回的列表所做的任何更改Collections.singletonList(something)都会导致
UnsupportedOperationException

同样,返回的列表的容量Collections.singletonList(something)将始终为
1,这Arrays.asList(something)与其后备阵列的容量不同。

Collections(二)List上篇-ArrayList

Collections(二)List上篇-ArrayList

List是一个有序的序列结构,基于 位置访问 ,List接口在Collection接口上新增加了一些方法,允许在指定位置插入和删除元素,也允许对元素的搜索(indexOf)。List是如何实现Iterator迭代器,ArrayList 和 LinkedList的性能差异为何会表现在不同方面,本文将对ArrayList进行详细分析。

List接口

我们先来看看List接口,提供了基于位置访问的一些方法。

方法 作用
E get(int index) 从某个位置获取元素
E set(int index, E element) 某个位置设置元素
void add(int index, E element) 某个位置增加元素
E remove(int index) 移除某个位置的元素
int indexOf(Object o) 搜索某个元素首次出现的位置
int lastIndexOf(Object o) 搜索某个元素最后出现的位置

ArrayList实现原理

image

ArrayList是基于可变数组实现的,内部维护了一个Object[] elementData数组,同时提供了操纵数组容量的方法,List的大小是由size变量决定的,容量是由数组的大小决定的,容量Capacity不满足size大小时,ArrayList就会自动通过合适的扩容策略进行扩容。

初始容量

ArrayList遵循上一节转换构造器的原则:提供了一个无参构造方法和Collection为参数的构造方法。

同时,还提供了一个int类型的参数来定义内部数组的容量public ArrayList(int initialCapacity),这个参数的初始容量默认是定义的一个常量:

/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

两个问题:1.ArrayList什么时候会去增长容量?2.它的容量增长策略是怎么实现的呢?

需要扩容的场景肯定是因为容量无法满足元素的个数了,所以在增加元素add的时候,或者增加集合元素addAll,或者以另一个集合初始化ArrayList的时候,都会去判断是否增加容量,本章以及接下来的所有章节增长容量策略的分析都将依据 增加元素 源码进行分析。

增加元素和增长容量策略分析

直接看源码,数组的复制使用了高效的System.arraycopy方法,参数可以是不同数组,也可以作用于同一个数组。

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!

    elementData[size++] = e;
    return true;
}

public void add(int index, E element) {
    rangeCheckForAdd(index); // 检查下标是否越界
    ensureCapacityInternal(size + 1);  // Increments modCount!!

    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

方法ensureCapacityInternal(size + 1);是为了容量增长策略设计的,我们看下这个方法:

private void ensureCapacityInternal(int minCapacity) {
  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
  }

  ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
  modCount++; // 这是为fail-fast机制设计的变量

  // overflow-conscious code
  if (minCapacity - elementData.length > 0)
    grow(minCapacity);
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;
  int newCapacity = oldCapacity + (oldCapacity >> 1);
  if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
  if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
  // minCapacity is usually close to size, so this is a win:
  elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
  if (minCapacity < 0) // overflow
    throw new OutOfMemoryError();
  return (minCapacity > MAX_ARRAY_SIZE) ?
    Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

这部分源码其实很好理解,我们能得出以下几个结论,这些结论很好的回答了上文中提到的两个问题:

  • if (minCapacity - elementData.length > 0) grow(minCapacity); 当元素个数size超过了内部维护的数组容量大小时,就会去扩容
  • int newCapacity = oldCapacity + (oldCapacity >> 1); 增加的容量是当前容量的二分之一,至于为什么是1/2,这应该是JDK设计者的一个折中选择问题
  • 采用Arrays.copyOf复制元素到新的空间,底层也是采用System.arraycopy方法

基础源码分析

获取元素

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

rangeCheck检测角标index是否越界合法。

更新元素

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

通过set方法设置元素,会返回老的元素值。

删除元素

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

和增加元素一样,删除元素的代码也采用了System.arraycopy方法进行数组移位可以得出结论,ArrayList在插入和删除操作会花费一定的代价,随机访问操作的时间复杂度将是常量级的

索引元素

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

iterator源码实现和fail-fast设计

private class Itr implements Iterator<E> {
  int cursor;     // index of next element to return
  int lastRet = -1; // index of last element returned; -1 if no such
  int expectedModCount = modCount;

  public boolean hasNext() {
    return cursor != size;
  }

  @SuppressWarnings("unchecked")
  public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
      throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
      throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
  }

  public void remove() {
    if (lastRet < 0)
      throw new IllegalStateException();
    checkForComodification();

    try {
      ArrayList.this.remove(lastRet);
      cursor = lastRet;
      lastRet = -1;
      expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
      throw new ConcurrentModificationException();
    }
  }

  @Override
  @SuppressWarnings("unchecked")
  public void forEachRemaining(Consumer<? super E> consumer) {
    Objects.requireNonNull(consumer);
    final int size = ArrayList.this.size;
    int i = cursor;
    if (i >= size) {
      return;
    }
    final Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length) {
      throw new ConcurrentModificationException();
    }
    while (i != size && modCount == expectedModCount) {
      consumer.accept((E) elementData[i++]);
    }
    // update once at end of iteration to reduce heap write traffic
    cursor = i;
    lastRet = i - 1;
    checkForComodification();
  }

  final void checkForComodification() {
    if (modCount != expectedModCount)
      throw new ConcurrentModificationException();
  }
}

Itr是ArrayList的一个内部类,所以可以通过ArrayList.this方式引用ArrayList内部域。

hasNext()和next()方法的实现就是不断向后移动下标cursor,直到cursor==size为止。

上一篇文章说过,ConcurrentModificationException是由迭代器抛出来的异常,从源码中我们可以看到,当modCount != expectedModCount时,就会提前抛出异常,即fail-fast,其中expectedModCount是在构造Itr对象时产生的modCount是存在ArrayList的一个变量,在每次新增元素、删除元素或者结构改变(比如排序等),就会累加modCount,上文ensureExplicitCapacity源码中就能看到modCount++操作。

我们已经知道,在迭代集合时,是不允许更改集合内部结构的,如果希望某个符合条件的元素删除呢?答案是使用java.util.Iterator.remove()方法

ArrayList提供了方法ListIterator<E> listIterator()获取ListIterator迭代器,这是一个为list打造的迭代器,允许双向遍历和更多操作。

public interface ListIterator<E> extends Iterator<E> {
    // Query Operations
    boolean hasNext();
    E next();
    boolean hasPrevious();
    E previous();
    int nextIndex();
    int previousIndex();

    // Modification Operations
    void remove();
    void set(E e);
    void add(E e);
}

性能

性能问题分为时间复杂度和空间复杂度两方面去讨论。

ArrayList实现了RandomAccess接口,它长于随机访问,不擅于插入和删除,这与LinkedList是相反的。

如果初始容量过大实际元素过少,则会造成空间的浪费。如果一次插入的元素很多,而初始容量不够,则会频繁的扩容,这无疑增加的时间复杂度。

为了性能考虑,在插入大量数据的时候,我们可以选择一个合适的初始容量,如果在一个初始容量已定的ArrayList中插入大量数据,可以通过调用方法`public void ensureCapacity(int minCapacity) 确保一次性扩容足够大的容量。

如果ArrayList中元素已经添加完毕,我们可以通过调用方法 public void trimToSize() 将内部数组的容量调整为实际元素的size大小来节约空间。

下篇文章,我们将会对LinkedList进行分析。

我们今天的关于Collections.emptyList而不是null检查?的分享就到这里,谢谢您的阅读,如果想了解更多关于android-kotlin.TypeCastException:无法将null强制转换为非null类型的kotlin.collections.Map、Arrays.asList()vs Collections.singletonList()、Arrays.asList()与Collections.singletonList()、Collections(二)List上篇-ArrayList的相关信息,可以在本站进行搜索。

本文标签: