我一直试图使用data.table::address
或.Internal(address())
跟踪内存中的各种对象,但是注意到有些对象每次返回相同的地址,而其他对象几乎总是不同的。这是怎么回事?
我注意到列表(data.tables,data.frames等)等对象的地址保持不变(由这些函数报告),而如果我尝试按[
报告地址进入一个列表,即address(lst[1])
我几乎每次都得到不同的结果。另一方面,lst[[1]]
返回相同的值,address(pi)
等常量的地址保持不变,而address(1)
是不稳定的。为什么会这样?
## Create some data.tables of different sizes and plot the addresses
library(data.table)
par(mfrow = c(2,2))
for (i in 2:5) {
dat <- data.table(a=1:10^i)
## Constants
addr1 <- address(dat)
addr2 <- address(dat[[1]])
addr3 <- address(dat$a) # same as addr2
## Vary
addrs <- replicate(5000, address(dat[1]))
plot(density(as.integer(as.hexmode(addrs))), main=sprintf("N: %g", nrow(dat)))
abline(v=as.integer(as.hexmode(c(addr1, addr2, addr3))), col=1:3, lwd=2, lty=1:3)
legend("topleft", c("dat", "dat[[1]]", "dat$a"), col=1:3, lwd=2, lty=1:3)
}
以下是我用不同大小的data.tables谈论的一些例子。它们只是address(dat[1])
(转换为整数)结果的密度,而这些行对应于data.table的常量地址。
答案 0 :(得分:3)
首先,我可以复制你的结果,所以我做了一些调查,并通过一些代码潜水。
当您使用dat
访问dat[1]
的第一个成员时,您实际上正在创建一个由list
或data[[1]]
中的dat$a
创建的切片。要获取切片,R首先复制列表,然后返回所需的切片。
所以 - 基本上 - 你看到你看到了什么,因为索引的[]
语法返回一个包含dat
的第一个元素的切片,它是{{1}的副本},它将位于任意内存位置。
dat$a
语法返回对实际列表的引用,该列表是[[]]
或data.table
中的列,因此其地址不变(或者至少在您更改之前该名单的成员)。
这可能令人困惑,因为当然执行data.frame
或类似操作会改变数据结构中列表的值。但是,如果您在进行此类更改之前和之后查看dat[1] = 6
,您会注意到实际上引用现在是另一个列表(副本),例如。
address(dat[[1]])
查看> dat <- data.table(a=1:10000)
> dat
a
1: 1
2: 2
3: 3
4: 4
5: 5
---
9996: 9996
9997: 9997
9998: 9998
9999: 9999
10000: 10000
> address(dat[[1]])
[1] "000000000CF389D8"
> address(dat[[1]])
[1] "000000000CF389D8"
> dat[1] = 100
> address(dat[[1]])
[1] "000000000D035B38"
> dat
a
1: 100
2: 2
3: 3
4: 4
5: 5
---
9996: 9996
9997: 9997
9998: 9998
9999: 9999
10000: 10000
>
(而不是data.frame
)的源代码,执行切片索引(data.table
)is here的代码,而直接索引({ {1}})is here。你可以看到后者更简单,并且长话短说,前者返回副本。如果直接更改切片(例如[]
),则会有一些logic here处理,以确保数据框现在引用更新后的副本。