The jdx data exchange functions use standard R and Java objects instead of custom classes for maximum compatibility. In some cases, it is not possible to know for certain what the intended target data structure should be. For example, should a collection of Java arrays be converted to an R list of vectors or a matrix? This section highlights conversion rules related to ambigous data structures. Developers who discover that convertToJava()
and convertToR()
are not always perfect inverses of each other will find the information here particularly useful.
Unnamed R Lists
If lst
is an R list, convertToR(convertToJava(lst))
may not result in a list. If lst
is an unnamed list, convertToJava(lst)
creates an object whose class implements java.util.Collection
. However, convertToR()
does not always create an unnamed list when it encounters a Java collection. There are several reasons for this, some of which are mentioned in Java Collections. We consider several cases below where an R list may be converted to Java, and then back to R, with unexpected results.
NOTE: Many of the common conversion issues related to unnamed lists can be avoided by using named lists.
A list of scalars (i.e., length-one vectors) converted to Java will be converted back to R as a vector if the data types are considered compatible. See the conversion table in Java Collections for data types that are considered compatible. In the following example, a list containing numeric, integer, and raw values, are converted to a Java collection containing double
, int
, and byte
values, respectively. The Java collection is converted back to R as a numeric vector because these number types are considered compatible.
lst <- list(1.1, 2L, as.raw(3))
(o <- convertToJava(lst))
## [1] "Java-Object{[1.1, 2, 3]}"
## [1] 1.1 2.0 3.0
## [1] "numeric"
In contrast, the list in the next example will be converted back to R as a list because the types are not considered compatible.
lst <- list(1.1, "a")
(o <- convertToJava(lst))
## [1] "Java-Object{[1.1, a]}"
## [[1]]
## [1] 1.1
##
## [[2]]
## [1] "a"
## [1] "list"
A list of compatible n-dimensional arrays will be converted back to R as an (n + 1)-dimensional array. (Note that vectors and factors are considered 1-dimensional arrays.) As with scalars, structures of mixed number types will assume the most generic number data type. This code returns a numeric matrix:
lst <- list(c(1, 2), c(3L, 4L))
(o <- convertToJava(lst, array.order = "column-major"))
## [1] "Java-Object{[[D@224aed64, [I@c39f790]}"
(r <- convertToR(o, array.order = "column-major"))
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
## [1] "matrix" "array"
Similarly, this next example results in numeric 3-dimensional array.
# This example results in a three-dimensional array.
lst <- list(matrix(1:4, 2, 2), matrix(5:8, 2, 2))
(o <- convertToJava(lst, array.order = "column-major"))
## [1] "Java-Object{[[[I@2ac1fdc4, [[I@5f150435]}"
convertToR(o, array.order = "column-major")
## , , 1
##
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
##
## , , 2
##
## [,1] [,2]
## [1,] 5 7
## [2,] 6 8
Perhaps the most unexpected result is when a single-member list is converted back to R as a vector or array. In this case, a nested list becomes a matrix:
lst <- list(list(1))
convertToR(convertToJava(lst))
## [,1]
## [1,] 1
Unnamed lists containing named lists of scalars will be converted to a data frame if the names are all the same and the data types are compatible.
lst <- list(
list(field1 = "a", field2 = 1),
list(field1 = "b", field2 = 2)
)
convertToR(convertToJava(lst))
## field1 field2
## 1 a 1
## 2 b 2
All of the conversion issues illustrated above can be avoided by using named lists in place of unnamed lists. For example, this list will remain a list.
lst <- list(a = 1.1, b = 2L, c = as.raw(3))
convertToR(convertToJava(lst))
## $a
## [1] 1.1
##
## $b
## [1] 2
##
## $c
## [1] 03
Named R Lists
Named R lists have fewer ambiguity issues than unnamed lists. The only exception applies to data frames. A named list of same-length vectors is converted to a Java map of arrays. This structure will be converted back to R as a data frame if it contains two or more arrays. The following example illustrates this behavior.
lst <- list(col1 = 1:3, col2 = letters[1:3])
convertToR(convertToJava(lst))
## col1 col2
## 1 1 a
## 2 2 b
## 3 3 c
A named list of length-one vectors is converted to a Java map of scalars by default. However, if length.one.vector.as.array = TRUE
, the object is converted to a map of arrays. This results in a data frame when converted back to R.
lst <- list(col1 = 1, col2 = "a")
convertToR(convertToJava(lst))
## $col1
## [1] 1
##
## $col2
## [1] "a"
lst <- list(col1 = 1, col2 = "a")
convertToR(convertToJava(lst, length.one.vector.as.array = TRUE))
## col1 col2
## 1 1 a