本篇文章给大家谈谈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的关键字)
- 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的关键字)
如果我在某个类中有一个很少使用的集合,该集合可能被实例化很多次,则有时我可能会求助于以下“习惯用法”以节省不必要的对象创建:
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
我一直在寻找可以广泛帮助我的代码的答案,但是我发现的解决方案对我不起作用.
我收到以下错误:
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()
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(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
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实现原理
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的相关信息,可以在本站进行搜索。
本文标签: