3

In Rust and Swift you can create a slice of either/both arrays and strings which doesn't make a new copy of the elements but gives you a view into the existing elements using a range or iterator and implemented internally a reference pair or reference + length.

Does Kotlin have a similar concept? It seems to have a similar concept for lists.

It's hard to Google since there is a function called "slice" which seems to copy the elements. Have I got it all wrong or is it missing?

(I'm aware I can work around it easily. I have no Java background btw.)

hippietrail
  • 15,848
  • 18
  • 99
  • 158
  • 1
    I'm new to Kotlin myself, but it looks like Kotlin (like Java) has only `pass by value` - [Link](https://stackoverflow.com/questions/44515031/is-kotlin-pass-by-value-or-pass-by-reference) – clamentjohn Jun 03 '21 at 04:32
  • Yes refs for passing but normal references are just a pointer. In the Rust world the ones for arrays and vecs are called something like "wide references" or "fat references' because they pass both a pointer and a length, though the user may see it as a start reference and an end reference. I think Kotlin lacks this. (I'm learning all three languages concurrently from a mostly C+JS background.) – hippietrail Jun 03 '21 at 04:37
  • 1
    @clmno Java is always *pass references by value* – Boris the Spider Jun 03 '21 at 04:47
  • 1
    `List.subList` is indeed a view on the underlying `List` - so it will make a “slice” without copying. There is no such functionality for `String` - `subString` will create a copy of the underlying `char[]`; this was changed to avoid an issue whereby people would slurp, for example, an entire file into memory then `subString` some small part - the small `String` would then hold the large one in memory. – Boris the Spider Jun 03 '21 at 04:51
  • @BoristheSpider: It seems you know your way around the language and that this would be the answer. If you post an answer I will accept it. – hippietrail Jun 03 '21 at 04:53
  • I don’t think it fully answers your question as there are different garbage collection implications of holding a view to simply referencing a subset of the memory. I don’t know enough about Swift, but Rust isn’t garbage collected. There are more subtleties here than just the functionality itself. – Boris the Spider Jun 03 '21 at 05:37
  • 1
    @BoristheSpider Note that neither `substring()` nor `subSequence()` method explicitly specifies that it doesn't create a view or that it copies the data. Since strings are immutable, I think it doesn't matter too much from the developers perspective, we should not really care. Java isn't a language where developers have full control of the memory layout, etc. - this is considered implementation details of the underlying VM/stdlib and it is hidden from developers. There could be an implementation of Java that e.g. create a view if substring is more than 80% of the original string. – broot Jun 03 '21 at 10:49
  • 1
    Where it starts to matter is when we have mutable data. In fact, `CharBuffer`, which is another implementation of `CharSequence`, but is mutable, has `slice()` method that creates a view. @hippietrail , if you can use `CharSequence` instead of Strings (they're similar to some degree) then you can choose to use `CharBuffer`. However, I'm not sure if it really makes sense. Copying of data isn't that bad in most cases. – broot Jun 03 '21 at 10:51
  • @broot: I'm parsing/analysing binary files. But mostly as a way to learn and compare a few modern languages. I'll play with `CharBuffer` and `CharSequence`. Thanks. – hippietrail Jun 03 '21 at 11:24
  • 1
    In this specific case Kotlin may be not-that-modern. Even if it is a separate language and compiles/transpiles to native and JS, it is still quite dependent on the design of Java/JVM. It wouldn't make too much sense to reinvent strings, so we are somewhat limited to how they where designed in Java. – broot Jun 03 '21 at 11:36

1 Answers1

2

I don't think so.

You can use asList to get a view of the Array as a list. Then the function you already found, subList works as usual. However, I'm not aware of an equivalent of subList for Arrays. The list returned from asList is immutable, so you cannot use it to modify the array. If you attempt to make the list mutable via toMutableList, it will just make a new copy.

fun main() {
    val alphabet = CharArray(26) { 'a' + it }
    println(alphabet.joinToString(", "))

    val listView = alphabet.asList().subList(10, 15)
    println(listView)

    for (i in alphabet.indices) alphabet[i] = alphabet[i].toUpperCase()
    println(alphabet.joinToString(", "))
    // listView is also updated
    println(listView)
}

Output:

a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
[k, l, m, n, o]
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
[K, L, M, N, O]

Using lists (or Collections) is preferred in Kotlin, and comes with much better support for generics and immutability than arrays.

If you need to mutate arrays, you probably have to manage it yourself, passing around (a reference to) the array and an IntRange of the indices you care about.

Adam Millerchip
  • 20,844
  • 5
  • 51
  • 74