前言
今天遇到一个问题,是关于 MutableList.addAll(specifiedCollection)。我发现执行addAll后,列表中的数据就会随着 specifiedCollection 一起变化,也就是说指向了同一内存引用地址。
上案例
我们ViewModel中有一个itemDataList,表示水果的价格,如:[ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
class TestViewModel : ViewModel() {
private val TAG = "TestViewModel"
private val itemDataList: MutableList<ItemData> = mutableListOf(
ItemData("西瓜", 3),
ItemData("苹果", 2),
)
private val _itemDataListMld: MutableLiveData<List<ItemData>> = MutableLiveData<List<ItemData>>().apply {
postValue(itemDataList)
}
val itemDataLd: LiveData<List<ItemData>> = _itemDataListMld
fun getItemDataList() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
//重复10次修改动作。
repeat(10) {
//随机修改列表中的数据
(0..1).random().let {
Log.e(TAG, "getItemDataList: random change index = $it")
itemDataList[it].price = (1..100).random()
}
_itemDataListMld.postValue(itemDataList)
delay(1000)
}
}
}
}
}
当调用ViewModel.getItemDataList()后,就会重复随机修改列表中的数据,并通过postValue将修改后的列表数据给到itemDataLd,通知给相对应的活跃观察者对象。
在Activity中进行观察该itemDataLd对象,在收到数据更新通知后,打印数据,并对未初始化的oldItemDataList进行初始化。
class TestActivity : AppCompatActivity() {
private val TAG = "TestActivity"
private lateinit var binding: ActivityTestBinding
private lateinit var viewModel: TestViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this)[TestViewModel::class.java]
binding.updateListBtn.setOnClickListener {
viewModel.getItemDataList()
}
val oldItemDataList: MutableList<ItemData> = mutableListOf()
var isInit = false
viewModel.itemDataLd.observe(this) { newItemDataList ->
Log.e(TAG, "onCreate: itemDataLd.observe oldItemDataList = $oldItemDataList")
Log.e(TAG, "onCreate: itemDataLd.observe newItemDataList = $newItemDataList")
//对于 oldItemDataList 只进行一次初始化
if (!isInit) {
oldItemDataList.clear()
val isAddAllData = oldItemDataList.addAll(newItemDataList)
Log.e(TAG, "onCreate: addALL ************************ isAddAllData = $isAddAllData")
isInit = true
}
}
}
}
通过上述代码,可以看出,我们只对 oldItemDataList 只进行一次初始化操作,接着后面收到新的newItemDataList数据通知也不会去修改 oldItemDataList。
由于我们在实例化_itemDataListMld对象时,会立即进行postValue(itemDataList),所以在进入Activity后会立马收到关于itemDataLd最新数据的通知,打印Log看看。
17:25:47.518 26784-26784 onCreate: itemDataLd.observe oldItemDataList = []
17:25:47.518 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
17:25:47.518 26784-26784 onCreate: addALL ************************ isAddAllData = true
通过Log可以看到,oldItemDataList 一开始是空的,接着对于进行初始化,addAll 操作返回 true,说明addAll操作成功。
接着,我们再触发一下 viewModel.getItemDataList 来打印一下Log看看。
17:25:59.310 26784-26965 getItemDataList: random change index = 1
17:25:59.315 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=21)]
17:25:59.315 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=21)]
17:26:00.321 26784-26965 getItemDataList: random change index = 0
17:26:00.323 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=39), ItemData(name=苹果, price=21)]
17:26:00.323 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=39), ItemData(name=苹果, price=21)]
17:26:01.325 26784-26965 getItemDataList: random change index = 0
17:26:01.327 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=63), ItemData(name=苹果, price=21)]
17:26:01.328 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=63), ItemData(name=苹果, price=21)]
17:26:02.330 26784-26965 getItemDataList: random change index = 0
17:26:02.332 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=75), ItemData(name=苹果, price=21)]
17:26:02.332 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=75), ItemData(name=苹果, price=21)]
17:26:03.335 26784-26965 getItemDataList: random change index = 1
17:26:03.337 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=75), ItemData(name=苹果, price=74)]
17:26:03.337 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=75), ItemData(name=苹果, price=74)]
17:26:04.338 26784-26965 getItemDataList: random change index = 1
17:26:04.340 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=75), ItemData(name=苹果, price=44)]
17:26:04.341 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=75), ItemData(name=苹果, price=44)]
17:26:05.344 26784-26965 getItemDataList: random change index = 0
17:26:05.347 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=22), ItemData(name=苹果, price=44)]
17:26:05.347 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=22), ItemData(name=苹果, price=44)]
17:26:06.350 26784-26965 getItemDataList: random change index = 1
17:26:06.353 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=22), ItemData(name=苹果, price=33)]
17:26:06.353 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=22), ItemData(name=苹果, price=33)]
17:26:07.355 26784-26965 getItemDataList: random change index = 0
17:26:07.359 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=91), ItemData(name=苹果, price=33)]
17:26:07.359 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=91), ItemData(name=苹果, price=33)]
17:26:08.360 26784-26965 getItemDataList: random change index = 0
17:26:08.362 26784-26784 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=46), ItemData(name=苹果, price=33)]
17:26:08.363 26784-26784 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=46), ItemData(name=苹果, price=33)]
通过Log可以看出,虽然我们没有对 oldItemDataList 进行新的操作,但是 oldItemDataList 的数据还是会随着LiveData推过来的新的List<ItemData>数据变化而变化。
这是为什么呢?🤔
有些同学可能对MutableList不熟悉,kotlin将集合类型分为了只读类型与可变类型。
- 只读类型:实现只读接口
Collection<out E>,提供访问集合元素的操作,。 - 可变类型:实现可变接口
MutableCollection<E>,通过写操作扩展相应的只读接口:添加、删除和更新其元素。
而看MutableList的初始化方法mutableListOf,可以发现本质上其实是ArrayList。
public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
所以,我们往下看看ArrayList的addAll方法。
ArrayList.java
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
先将要添加的结合转换为数组a,然后通过System.arraycopy()方法将数组a的元素拷贝到elementData数组中。
System.arraycopy()是Java中的一个方法,用于数组之间的元素拷贝。但是该方法拷贝的是数组元素的内存引用地址,所以是一个浅拷贝方法。
这就涉及到了浅拷贝知识。
关于浅拷贝与深拷贝
- 浅拷贝:创建一个新对象,指向被复制对象的内存引用地址。
- 深拷贝:创建一个新对象,且创建一个新的内存地址,并将被复制对象的值复制到新对象中。
举个例子
我们为需要展示的商品创建一个bean文件,包含商品名称、价格以及来源这几个属性,其中价格与来源的城市是可变属性,其它都是不可变属性。
/**
* 商品数据
*/
data class ItemData(
val name: String,//不可变的基本数据类型属性
var price: Int,//可变的基本数据类型属性
val source: Source//不可变的引用类型属性
)
/**
* 来源地址
*/
data class Source(
var city: String//可变的基本数据类型属性
)
我们先通过ItemData来创建我们的第一个商品:苹果。
val originalUser = ItemData("苹果", 20, Source("烟台"))
接着,我们试着使用浅拷贝来创建另一个商品:香蕉。
val copiedUser = originalUser.copy(name = "香蕉")
打印这两个商品出来看看,分别是:
originalUser = ItemData(name=苹果, price=20, source=Source(city=烟台))
copiedUser = ItemData(name=香蕉, price=20, source=Source(city=烟台))
接着,我们修改一下香蕉的来源信息,将香蕉的来源城市修改为"海南"。
println("********* 修改来源 *********")
copiedUser.source.city = "海南"
修改后,再次打印两个商品:
********* 修改来源 *********
originalUser = ItemData(name=苹果, price=20, source=Source(city=海南))
copiedUser = ItemData(name=香蕉, price=20, source=Source(city=海南))
可以看出,虽然我们只修改了香蕉的来源信息,但是,苹果的来源也随之更改了。
接着,我们再来修改一下香蕉的价格,将香蕉的价格修改为9999。
println("********* 修改价格 *********")
copiedUser.price = 9999
思考一下🤔,这次,苹果的价格也会随着香蕉一起更改为9999吗?
上答案!再次打印两个商品:
********* 修改价格 *********
originalUser = ItemData(name=苹果, price=20, source=Source(city=海南))
copiedUser = ItemData(name=香蕉, price=9999, source=Source(city=海南))
通过Log可以看出,这次,苹果的价格并没有随之一起更改。
这是为什么呢?🤔
我们先来看看 data class copy() 方法。
data class copy方法
首先 copy 方法是浅拷贝方法,而且是会对主构造函数中的所有属性进行浅拷贝。
但是属性又分基本数据类型与引用类型,这两者是有差异的。
- 基本数据类型:新对象在浅拷贝时会直接复制被拷贝对象的基本数据类型值,但这两者之间是独立的值,也就是说,修改这两者之间的任何一个对象的基本数据类型属性都不会影响到另外一个对象。
- 引用类型:新对象在浅拷贝时会复制被拷贝对象的引用。这两者对象持有同一内存引用地址,所以是关联的。也就是说,修改这两者之间的任何一个对象的属性,另外一个对象也会跟着一起发生更改。
所以在上方的例子中,price为可变的基本数据类型属性,拷贝对象的值是独立的,所以当我们更改 copiedUser.price = 9999 时,originalUser.price 并不会随之一起更改。而source为不可变的引用类型属性,浅拷贝对象与被拷贝对象是关联的,所以当我们更改 copiedUser.source.city = "海南" 时,originalUser.source 会随之一起更改。
关于originalUser 与 copiedUser 的内存地址关系图,如下所示👇
解决浅拷贝问题
OK,回到初始List.addAll()方法,如果我们想解决其浅拷贝问题,我们可以这么做。
TestActivity.java
val oldItemDataList: MutableList<ItemData> = mutableListOf()
var isInit = false
viewModel.itemDataLd.observe(this) { newItemDataList ->
Log.e(TAG, "onCreate: itemDataLd.observe oldItemDataList = $oldItemDataList")
Log.e(TAG, "onCreate: itemDataLd.observe newItemDataList = $newItemDataList")
if (!isInit) {
oldItemDataList.clear()
//使用循环遍历复制
newItemDataList.forEach { itemData ->
//使用copy()方法拷贝一个新对象
val addResult = oldItemDataList.add(itemData.copy())
Log.e(TAG, "onCreate: add ${itemData.name} ${itemData.price} result is -> $addResult")
}
isInit = true
}
}
循环遍历新数据,并使用copy()方法拷贝一个新对象出来,然后add进oldItemDataList。
打印一下Log看看:
09:40:49.830 25724-25724 onCreate: itemDataLd.observe oldItemDataList = []
09:40:49.830 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:49.830 25724-25724 onCreate: add 西瓜 3 result is -> true
09:40:49.831 25724-25724 onCreate: add 苹果 2 result is -> true
09:40:51.435 25724-25933 getItemDataList: random change index = 1
09:40:51.448 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:51.448 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=100)]
09:40:52.445 25724-25935 getItemDataList: random change index = 1
09:40:52.447 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:52.447 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=54)]
09:40:53.450 25724-25935 getItemDataList: random change index = 0
09:40:53.454 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:53.454 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=51), ItemData(name=苹果, price=54)]
09:40:54.453 25724-25935 getItemDataList: random change index = 1
09:40:54.455 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:54.455 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=51), ItemData(name=苹果, price=91)]
09:40:55.458 25724-25935 getItemDataList: random change index = 0
09:40:55.460 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:55.461 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=74), ItemData(name=苹果, price=91)]
09:40:56.462 25724-25935 getItemDataList: random change index = 0
09:40:56.463 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:56.463 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=11), ItemData(name=苹果, price=91)]
09:40:57.467 25724-25934 getItemDataList: random change index = 0
09:40:57.470 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:57.471 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=54), ItemData(name=苹果, price=91)]
09:40:58.472 25724-25934 getItemDataList: random change index = 1
09:40:58.473 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:58.474 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=54), ItemData(name=苹果, price=96)]
09:40:59.475 25724-25934 getItemDataList: random change index = 0
09:40:59.477 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:40:59.478 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=50), ItemData(name=苹果, price=96)]
09:41:00.480 25724-25934 getItemDataList: random change index = 1
09:41:00.483 25724-25724 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]
09:41:00.484 25724-25724 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=50), ItemData(name=苹果, price=73)]
通过Log可以看出,oldItemDataList只会被初始化一次,并且不会跟随着新数据一起更新内容了,始终维持着初始数据[ItemData(name=西瓜, price=3), ItemData(name=苹果, price=2)]。
不知你内心有没有疑惑?copy()方法不是浅拷贝方法吗?拷贝出来的新对象与旧对象不是持有同一内存引用地址么,怎么旧对象内容数据不会随着新对象一起更改呢?
因为在ItemData属性中,虽然price是可变属性,但也是可变的基本数据类型属性,拷贝对象拥有独立的值。
data class ItemData(
val name: String,
var price: Int//可变的基本数据类型属性
)
我们可以进一步测试一下,给ItemData添加上source来源,定义为不可变引用类型属性。
data class ItemData(
val name: String,
var price: Int,//可变的基本数据类型属性
val source: Source//不可变引用类型属性
)
data class Source(
var city: String
)
然后为我们的初始化数据添加上source属性。
private val itemDataList: MutableList<ItemData> = mutableListOf(
ItemData("西瓜", 3, Source("宁夏")),
ItemData("苹果", 2, Source("烟台"))
)
接着,我们修改一下getItemDataList()方法,随机修改价格与来源数据。
fun getItemDataList() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
repeat(10) {
(0..1).random().let { index ->
Log.e(TAG, "getItemDataList: random change index = $it")
(1..100).random().let { randomPrice ->
itemDataList[index].apply {
//随机修改价格
price = (1..100).random()
//随机修改来源数据
source.city = "${source.city} + $randomPrice"
}
}
}
_itemDataListMld.postValue(itemDataList)
delay(1000)
}
}
}
}
打印Log查看一下:
11:08:42.304 8184-8184 onCreate: itemDataLd.observe oldItemDataList = []
11:08:42.304 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
11:08:42.304 8184-8184 onCreate: add 西瓜 3 result is -> true
11:08:42.304 8184-8184 onCreate: add 苹果 2 result is -> true
11:08:54.210 8184-8440 getItemDataList: random change index = 1
11:08:54.215 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85))]
11:08:54.215 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=32, source=Source(city=烟台 + 85))]
11:08:55.220 8184-8440 getItemDataList: random change index = 0
11:08:55.223 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85))]
11:08:55.223 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=33, source=Source(city=宁夏 + 29)), ItemData(name=苹果, price=32, source=Source(city=烟台 + 85))]
11:08:56.224 8184-8440 getItemDataList: random change index = 0
11:08:56.226 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85))]
11:08:56.227 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=26, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=32, source=Source(city=烟台 + 85))]
11:08:57.227 8184-8440 getItemDataList: random change index = 1
11:08:57.228 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85 + 19))]
11:08:57.228 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=26, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=60, source=Source(city=烟台 + 85 + 19))]
11:08:58.233 8184-8440 getItemDataList: random change index = 1
11:08:58.235 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85 + 19 + 78))]
11:08:58.236 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=26, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=75, source=Source(city=烟台 + 85 + 19 + 78))]
11:08:59.237 8184-8443 getItemDataList: random change index = 1
11:08:59.239 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85 + 19 + 78 + 52))]
11:08:59.240 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=26, source=Source(city=宁夏 + 29 + 19)), ItemData(name=苹果, price=42, source=Source(city=烟台 + 85 + 19 + 78 + 52))]
11:09:00.242 8184-8443 getItemDataList: random change index = 0
11:09:00.245 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19 + 14)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85 + 19 + 78 + 52))]
11:09:00.245 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=24, source=Source(city=宁夏 + 29 + 19 + 14)), ItemData(name=苹果, price=42, source=Source(city=烟台 + 85 + 19 + 78 + 52))]
11:09:01.247 8184-8443 getItemDataList: random change index = 1
11:09:01.249 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19 + 14)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85 + 19 + 78 + 52 + 14))]
11:09:01.250 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=24, source=Source(city=宁夏 + 29 + 19 + 14)), ItemData(name=苹果, price=52, source=Source(city=烟台 + 85 + 19 + 78 + 52 + 14))]
11:09:02.252 8184-8443 getItemDataList: random change index = 0
11:09:02.255 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19 + 14 + 9)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85 + 19 + 78 + 52 + 14))]
11:09:02.255 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=40, source=Source(city=宁夏 + 29 + 19 + 14 + 9)), ItemData(name=苹果, price=52, source=Source(city=烟台 + 85 + 19 + 78 + 52 + 14))]
11:09:03.255 8184-8443 getItemDataList: random change index = 1
11:09:03.257 8184-8184 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏 + 29 + 19 + 14 + 9)), ItemData(name=苹果, price=2, source=Source(city=烟台 + 85 + 19 + 78 + 52 + 14 + 52))]
11:09:03.257 8184-8184 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=40, source=Source(city=宁夏 + 29 + 19 + 14 + 9)), ItemData(name=苹果, price=44, source=Source(city=烟台 + 85 + 19 + 78 + 52 + 14 + 52))]
通过Log可以看出,oldItemDataList 中的ItemData.source是会跟随新数据一起更新。也进一步证实了使用浅拷贝的引用类型属性,拷贝对象与被拷贝对象之间持有同一内存引用地址。
如果,我们想对引用类型属性进行深拷贝操作,也就是对案例中的source属性进行深拷贝,我们可以这么操作。
var oldItemDataList: List<ItemData> = listOf()
var isInit = false
viewModel.itemDataLd.observe(this) { newItemDataList ->
Log.e(TAG, "onCreate: itemDataLd.observe oldItemDataList = $oldItemDataList")
Log.e(TAG, "onCreate: itemDataLd.observe newItemDataList = $newItemDataList")
if (!isInit) {
//手动进行设置Source属性
oldItemDataList = newItemDataList.map { it.copy(source = Source(it.source.city)) }
isInit = true
}
}
打印Log看看:
20:17:33.655 9198-9198 onCreate: itemDataLd.observe oldItemDataList = []
20:17:33.655 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:36.706 9198-9945 getItemDataList: random change index = 1
20:17:36.712 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:36.712 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=45, source=Source(city=烟台 + 100))]
20:17:37.714 9198-9945 getItemDataList: random change index = 1
20:17:37.715 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:37.715 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=34, source=Source(city=烟台 + 100 + 68))]
20:17:38.717 9198-9945 getItemDataList: random change index = 1
20:17:38.719 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:38.719 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=10, source=Source(city=烟台 + 100 + 68 + 10))]
20:17:39.719 9198-9945 getItemDataList: random change index = 1
20:17:39.719 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:39.719 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=6, source=Source(city=烟台 + 100 + 68 + 10 + 21))]
20:17:40.721 9198-9945 getItemDataList: random change index = 1
20:17:40.722 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:40.722 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=99, source=Source(city=烟台 + 100 + 68 + 10 + 21 + 9))]
20:17:41.730 9198-9947 getItemDataList: random change index = 1
20:17:41.732 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:41.733 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=8, source=Source(city=烟台 + 100 + 68 + 10 + 21 + 9 + 89))]
20:17:42.735 9198-9945 getItemDataList: random change index = 1
20:17:42.736 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:42.737 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=61, source=Source(city=烟台 + 100 + 68 + 10 + 21 + 9 + 89 + 85))]
20:17:43.740 9198-9945 getItemDataList: random change index = 1
20:17:43.744 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:43.744 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=19, source=Source(city=烟台 + 100 + 68 + 10 + 21 + 9 + 89 + 85 + 40))]
20:17:44.746 9198-9945 getItemDataList: random change index = 1
20:17:44.749 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:44.749 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=19, source=Source(city=烟台 + 100 + 68 + 10 + 21 + 9 + 89 + 85 + 40 + 51))]
20:17:45.752 9198-9945 getItemDataList: random change index = 0
20:17:45.755 9198-9198 onCreate: itemDataLd.observe oldItemDataList = [ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))]
20:17:45.755 9198-9198 onCreate: itemDataLd.observe newItemDataList = [ItemData(name=西瓜, price=87, source=Source(city=宁夏 + 40)), ItemData(name=苹果, price=19, source=Source(city=烟台 + 100 + 68 + 10 + 21 + 9 + 89 + 85 + 40 + 51))]
通过Log可以看出,oldItemDataList的数据不会随着新数据一起更新,始终保持着初始化数据[ItemData(name=西瓜, price=3, source=Source(city=宁夏)), ItemData(name=苹果, price=2, source=Source(city=烟台))],说明完成了对引用类型属性的深拷贝操作。
当拷贝的对象是可变的引用类型属性时,进行浅拷贝无法保证数据一致性。因为浅拷贝仅仅是拷贝对象的内存引用地址,多个对象引用同一个内存引用地址,这在多线程的环境下,就无法保证数据一致性了。所以针对可变的引用类型属性,就应该对其进行深拷贝,让复制的对象拥有独立的内存地址,保证其唯一性,从而避免出现一些奇怪的Bug。
总结
其实知道了原理之后,事情就变得简单了。当我们不想oldItemDataList中的元素数据跟随newItemDataList元素数据一起更改时,我们只需要保证两者的元素数据不是持有同一内存引用地址即可。所以,撇开深浅拷贝方法,化繁为简,我们可以直接实例化一个新对象出来,从而保证其内存引用地址的唯一性。
本文是由MutableList.addAll()延伸出来的学习文章,涉及到浅拷贝深拷贝以及data class的一些相关知识点,也是属于夯实基础的一篇文章,相信掌握了这些小知识点后,对日后分析问题会有很大的帮助。
到此本篇文章就结束啦,如果你有任何疑问或者不同的想法,欢迎在评论区留言与我一起探讨。
其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。
另外,如果你觉得文章不错,对你有所帮助,请帮我点个赞,就当鼓励,谢谢