Firstly, I can replicate your results, so I did a little investigation and dived through some kind of code.
When you access the first dat element using dat[1] , you actually create a slice from list to data[[1]] or dat$a . To take a slice, R first copies the list, and then returns the desired fragment.
So - basically - you see what you see, because the syntax [] for indexing returns a slice containing the first dat element , which is a copy of dat$a , which will be in a random location memory.
The syntax [[]] returns a link to the actual list, which is a column in your data.table or data.frame , and therefore its address is invariant (or at least until you change the member of this list )
This can be confusing because of course dat[1] = 6 or the like will change the value of the list in your data structure. However, if you look at address(dat[[1]]) before and after making such a change, you will notice that in fact the link now refers to another list (copy), for example.
> 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 >
Considering the source code for data.frame (rather than data.table ), the code that indexes the fragment ( [] ) is here , while the direct indexing ( [[]] ) is here . You can see that the latter is simpler and shorten the long history, the former returns a copy. If you change the slice directly (for example, dat[1] = 5 ), there is logic here that can guarantee that the data frame now refers to the updated copy.
source share