0

Say I have this:

class NumWithSuccessor {
    var num = 1
    val successor
        get() = num + 1
}

Now, if I want an array of nums instead,

class NumsWithSuccessors {
    var nums = Array<Int>(3){ 1 }
    val successor
        get() = /* What? */
}

My first guess is to use

get() = { Array<Int>(3){ nums[it] + 1 } }

But that would lead to creation of a new array every time I need to access a successor. Is there a simple, better way?

Practical Example:

// Need to go from this...
private val _dayWiseEventsList =             // For internal use
        MediatorLiveData<List<Event>>()

val dayWiseEventsList: LiveData<List<Event>> // For immutable, external observation
        get() = _dayWiseEventsList


// ... to this
private val _dayWiseEventsListArray =        // For internal use
        Array<MediatorLiveData<List<Event>>>(DAYS) { MediatorLiveData() }

val dayWiseEventsListArray                   // For immutable, external observation
        // Need an alternative for this
        get() = Array<LiveData<List<Event>>>(DAYS) { _dayWiseEventsListArray[it] }

4 Answers 4

1
class NumsWithSuccessors {
    var nums: List<Int> = List(3){ 1 }
    val successors: List<Int> = object: AbstractList<Int>() {
        override val size get() = nums.size
        override fun get(index: Int) = nums[index] + 1;
    }
}

In the above example, successors is a virtual List that doesn't actually contain data. When you try to read an item from successors, it simply looks at the underlying nums list to calculate the value. The AbstractList class deals with providing implementations for all the other list behaviour, based on the get and size implementations you supply.

This works with a List, but not with an Array. An array can't have custom behaviour; it's a primitive data type that just stores values. List is an interface, the underlying behaviour of which can be modified at will.

Sign up to request clarification or add additional context in comments.

Comments

1

You can lazily initialize it:

val successor by lazy { Array<Int>(3){ nums[it] + 1 } } 

This will create the array only once when it is first accessed, then it will return the same array on subsequent accesses.

However note that this will not update successor in case you modify nums. If you want to update it you will need to set a customer setter for nums and update successor accordingly.


Like I explained in the comments, I would make all this immutable, for example:

data class NumsWithSuccessors(val nums: List<Int> = List(3){ 1 }) {
    val successor by lazy { nums.map { it + 1 } }
}

8 Comments

The only downside here is that array is mutable, the successor will share that array outside, where it can be changed. Kotlin List<T> will be more safe here
@EugenePetrenko I totally agree, however OP is using arrays and asking arrays.
I'd say your solution is a big downside if successor must be dependent on nums not only during first access.
@Sergey Yes, however it is the OP that has put those restrictions. There's no way to actually solve his problem without making everything immutable, which is what I would do.
@Sergey It is not wrong, it does answer OP's question with the required restrictions. Is it correct? No. Is it how I would do it? Definitely not. But I can't fix without removing the question restrictions, which I already state in my answer.
|
0

You can create a setter for nums, and set successor value inside it using map operator. It prevents from creating a new array every time:

class NumsWithSuccessors {
    var successor: Array<Int> = arrayOf()
    var nums: Array<Int> = Array(3) { 1 }
    set(value) {
        field = value; successor = value.map { it + 1 }.toTypedArray()
    }

1 Comment

New list is also created in that case :). value.map creates a new list. Also the downside is that you need to change val modifier to var, and successor will be eligible for changing from outside.
0

successor can't depend on nums, has val modifier and use cached array at the same time. The solution is to create a new list using map function every time successor property is accessed:

class NumWithSuccessor {
    var nums = Array<Int>(3){ 1 }
    val successor
        get() = nums.map { it + 1 }
}

8 Comments

Still creates a new list every time it is called.
I guess in this case new array always should be created.
@m0skit0, you can create a setter for nums, and set successor value inside it using map operator. It prevents from creating a new array every time.
@MamykinAndrey Right, you can post that answer :)
> still creates a new list The list you create depends on the nums variable, which is also mutable. Caching it may harm at some point If you indeed want it to cache, you may remove get() = from the declaration. It will create a property with the value, that is computed when an instance is created
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.