How to create a generic array with nullable values in kotlin - arrays

Okay, so to try to learn the kotlin language, I am trying to implement a heap.
class Heap <T : Comparable<T>>(private var heap: Array<T>, private var size: Int){
I am having some difficulties with how the nullable types behave. In the beginning I gave a array of elements, but I started having difficulties when I wanted to allocate more elements
private fun increaseSize(){
if(size == heap.size){
//okay this is garbage
var enlargedHeap = arrayOfNulls<Comparable<*>>(heap.size * 2) as Array<T>
System.arraycopy(heap, 0, enlargedHeap, 0, size)
heap = enlargedHeap
}
}
So here I am guessing I am changing the heap from Array<T> to Array<T?> which makes sense. So I changed the constructor as well, to take Array<T?> and it suggests some pretty interesting things wherever I try to access heap[x]
else heap[pos1].compareTo(heap[pos2]) < 0
to
else heap[pos2]?.let { heap[pos1]?.compareTo(it) }!! < 0
then I looked at a tooltip stating that arrayOfNulls<Comparable<*>>(heap.size * 2) as Array<T> returned Array<T>
But when doing the arrayOfNulls it indeed returns an array with null values. However I get an error if I try to heap[x] = null
stating
Null cannot be a value of non-null type T
If I change the constructor to take a Array<T?>, then input arrays have to explicitly nullable too.
Which I fixed with
class Heap <T : Comparable<T>>{
private var heap: Array<T?>
private var size = 0
constructor(heap: Array<T>, size){
this.heap = heap as Array<T?>
this.size = size
}
But now it does not accept arrays that has null values when I try to instantiate it with an array with nullable values
var arr = arrayOfNulls<String>(9)
var heap = Heap(arr, arr.size-1)
So now I need two constructors? What is going on
Type mismatch. Required: Comparable Found:String?
Even with Àrray<T?> there is errors with the compareTo's not accepting nullable values.
Even with checks it still gives an error
return if(pos2 > size || (heap[pos1] == null || heap[pos2] == null)) false
else heap[pos1].compareTo(heap[pos2]) < 0
Type mismatch. Required:T Found: T?
How do I allow the array to contain null values?
Hopefully without making Array<T> nullable since it breaks the compareTo's and multiple constructors seems like bad design.

Arrays are tricky to work with in a generic class because they have reified types.
I think you need to move the backing array property out of the constructor so the constructor won't be ambiguous about nullability. Otherwise type inference won't work with the constructor, and you would also be forced to use a nullable-typed array as the constructor parameter even if you want the heap to have a non-nullable type. Anyway, it's good that you copy the array from the constructor rather than using it directly as a backing array, because otherwise there is the possibility that some outside object could still be modifying that backing array.
Since Arrays always have reified type, I think you also need to use a backing array of type Any?. This will mean you have to cast objects that are passed out of the heap in the public functions. You can make a single get function for that, and use it internally with this[] to avoid having to put casts all over your class. The unchecked casting is safe as long as you don't put anything besides a T in the part of the array below size, and you don't try to get values above size, since they're always null and T might not be nullable.
class Heap <T : Comparable<T>>(initialValues: Array<T>, private var size: Int) {
init {
if (size > initialValues.size)
error("Initial array is too small for initial size.")
}
private var heap = Array<Any?>(size){ initialValues[it] }
#Suppress("UNCHECKED_CAST")
private operator fun get(index: Int) = heap[index] as T
private fun increaseSize(){
var enlargedHeap = arrayOfNulls<Any>(heap.size * 2)
System.arraycopy(heap, 0, enlargedHeap, 0, size)
heap = enlargedHeap
}
fun pop(): T {
if (size == 0)
error("Cannot pop when size is 0")
size--
val out = this[size] // Use this class's getter to avoid having to cast
heap[size] = null
return out
}
fun push(value: T) {
if (size == heap.size)
increaseSize()
heap[size] = value
size++
}
}
You might also consider moving the size parameter out of the constructor to make usage simpler and avoid the ambiguous case of what happens if the input size is smaller than the array you pass in.
class Heap <T : Comparable<T>>(initialValues: Array<T>) {
private var size = initialValues.size
private var heap = Array<Any?>(size){ initialValues[it] }
//...

Please use Array<T?>, instead of Array<T>.
E.g. in your class signature, properties should be:
class Heap <T : Comparable<T>>(private var heap: Array<T?>, private var size: Int) {
If you need input with not null values, please use something like this:
private var heap = initialHeap.toArray(); // copy input parameter to internal array

Related

Kotlin ClassCastException when storing array in variable

I created a RollingWindow class in order to have a fixed number of most recent data points stored in an array.
class RollingWindow<T> (private val length: Int) {
private val window = Array<Any?>(length) {null}
private var count = 0
fun push(t: T) {
if (length == 0)
return
window[currentIndex()] = t
count++
}
#Suppress("UNCHECKED_CAST")
fun toArray(): Array<T> {
if (length == 0)
return arrayOf<Any>() as Array<T>
val firstHalf = window
.copyOfRange(currentIndex(), window.size)
.filterNotNull()
.toTypedArray()
val secondHalf = window
.copyOfRange(0, currentIndex())
.filterNotNull()
.toTypedArray()
val arr = arrayOf(*firstHalf, *secondHalf) as Array<T>
print(arr.contentToString())
//this works fine but for some reason the class cast exception is thrown from the unit test
return arr
}
override fun toString() = toArray().contentToString()
private fun currentIndex() = count % length
}
I wrote some unit tests and am getting a ClassCastException
#Test
fun testRollingWindowNotFull() {
val doubleWindow = RollingWindow<Double>(5)
doubleWindow.push(2.5)
doubleWindow.push(6.8)
assertArrayEquals(arrayOf(2.5, 6.8), doubleWindow.toArray()) //this passes
val variableInWindow = RollingWindow<Double>(5)
variableInWindow.push(25.6)
variableInWindow.push(24.32)
val windowArray = variableInWindow.toArray() // ClassCastException only gets thrown here or whenever it's stored in a variable. If I use variableInWindow.toArray() inline it's fine, as shown in previous assertion
assertArrayEquals(arrayOf(25.6, 24.32), windowArray) // This never gets reached
}
While running the test I tried casting the Array<Any> into an Array<T>. Casting within the RollingWindow class works fine, no errors, but I specifically get an error in the Unit Test. Here's the StackTrace:
Sep 13, 2021 1:55:49 PM org.junit.platform.launcher.core.EngineDiscoveryOrchestrator lambda$logTestDescriptorExclusionReasons$7
INFO: 0 containers and 4 tests were Method or class mismatch
[Ljava.lang.Object;#7a8051ce[Ljava.lang.Object;#3ba12a08[Ljava.lang.Object;#725e196
class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Double; ([Ljava.lang.Object; and [Ljava.lang.Double; are in module java.base of loader 'bootstrap')
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Double; ([Ljava.lang.Object; and [Ljava.lang.Double; are in module java.base of loader 'bootstrap')
at collections.RollingWindowTest.testRollingWindowNotFull(RollingWindowTest.kt:24)
Unlike all other generics in classes in Kotlin, the Array class and the Array class only has a reified type, and it is an invariant type. You cannot ever successfully cast an Array of one type to an Array of another type unless you are casting it to have a covariant or contravariant type.
I think the only reason the first part of your test passes is that there must be a compiler optimization that omits the reified cast inside the toArray() function if the returned object is used as an Any or Array<out Any>. The Java-defined assertArrayEquals function takes two Object[] parameters which get mapped to either Array<Any> or Array<out Any>, and perhaps in this case it's doing the latter cast because it sees that nothing is put into the arrays in this function.
So the compiler as an optimization may be replacing your reified cast to Array<Double> with a non-reified cast to Array<out Double>, which is a safe cast.
You might want to consider using List or MutableList instead of Array to avoid having to deal with these problems.

How to copy a pointer to an array in Swift?

I'm basically trying to wrap a C structure within a Swift class.
The Swift class has an instance property of type of the C structure.
The C structure contains several properties which are of type const char *.
In order to assign values to the structure, I wrote getters and setters for each property:
public class MyClass: NSObject {
private var configuration = c_structure_config()
}
// Function of MyClass
// f.ex. on "registrationUri:"
private(set) public var registrationUri: String {
get {
return String(cString: configuration.reg_uri)
}
set {
if (configuration.reg_uri != nil) {
configuration.reg_uri.deallocate()
}
let maxLength = newValue.count + 1 // +1 for NULL
var buffer: [CChar] = [CChar](UnsafeMutableBufferPointer<Int8>.allocate(capacity: maxLength))
guard newValue.getCString(&buffer, maxLength: maxLength, encoding: .utf8) == true else {
fatalError("Could not allocate memory for Account Config reg uri")
}
// "configuration" is the instance property (see above)
// reg_uri is of type char const *
configuration.reg_uri = UnsafePointer<Int8>(buffer)
}
}
However, this approach leads to weird crashes and error reports complaining about pointers overlapping range (Fatal error: UnsafeMutablePointer.initialize overlapping range).
I know that I'm deallocating and allocating memory whenever the string is set and that that's probably not very efficient. I haven't found a better solution so far though.
What's wrong here (or is this right, I made a wrong assumption and I gotta search somewhere else)?
There are a couple of problems with your solution.
First, it is ill-advised to use String.count to count to get the size of the underlying C buffer. The reason is that String.count returns the number of actual characters of your string, and not the number of bytes used to represent it. Not all characters fit the 256 bits of Int8 (a.k.a. CChar). Hence, your code will probably crash if you attempt setting your property with non-ascii characters. It is better to use the String.utf8CString property, which returns a contiguous array of CChar.
Second, since your buffer is allocated within the setter, it will be deallocated when your setter returns (Swift arrays are instances of value types, who's lifetime is bound by the stack). That means the pointer corresponding to buffer's base is actually invalidated when your setter returns. I suspect this is the reason why the error you reported occurs at runtime.
Finally, please do not test for true in guards and if statements.
Here is a corrected version:
var registrationUri: String {
get {
return String(cString: reg_uri)
}
set {
if (reg_uri != nil) {
reg_uri.deallocate()
}
newValue.withCString { cString in
// No need to add 1, newValue.utf8CString already has the correct buffer capacity.
let capacity = newValue.utf8CString.count
let buffer: UnsafeMutablePointer<Int8> = .allocate(capacity: capacity)
buffer.assign(from: cString, count: capacity)
reg_uri = UnsafePointer(buffer)
}
}
}
// ...
myInstance.registrationUri = "こんいちは"
print(myInstance.registrationUri)
// Prints "こんいちは"

Is array declaration in Kotlin with size possible just with initialization?

I didn't find how to declare in Kotlin an array with predefined size without initializate it.
This is OK:
lateinit var v:Array<Int>
But I guess that one cannot put a size specification in array type.
If one needs to specify a size. one have to do:
var v2:Array<Int> = Array<Int>(2){5}
In this case, all elements of the vector are equal to 5
Below there is a example with classes and arrays:
data class But(
val fufo: Int=0,
val tp: Int = 1
)
typealias ArBut = Array<But>
data class CArray (
var arrayC: ArBut = ArBut(2){But()}
)
val a = CArray(arrayOf(But(2,2),But(5,4),But(3,3)))
println(a.arrayC[2])
It works! The interesting part is that how the initialization is not part of type, you can put arrays of any size in the class without bounding check. It would be different if the size was part of type spec.
Now an exemple, using matrix. Notice that the syntax is a little bit intricate.
data class CMatr (
val matrC: Array<ArBut> = Array<ArBut>(2){ArBut(0){But()}}
)
val m = CMatr(arrayOf( arrayOf(But(2,2),But(5,4)),
arrayOf(But(-2,2),But(3,4)), arrayOf(But(1,1),But(5,3)) ))
println(m.matrC[2][1]) // Also works!
Is it impossible put size in array type specification or I'm missing something?
for primitive types :
this is how you do it. instead of using kotlin built-in functions like intArrayOf(args...) you use the constructor for IntArray
here is the example :
// Array of integers of a size of N
val arr = IntArray(N)
// Array of integers of a size of N initialized with a default value of 2
val arr = IntArray(N) { 2 }
for reference types :
for reference type objects you can do
val cars: Array<Car?> = arrayOfNulls(N)
//returns an array of nullable Car objects with null values and size of N
and if you want to an array of non null objects you need to initialize them when creating array
val cars: Array<Car> = Array<Car>(5){ Car() }
//returns an array of non nullable car objects that has been initialized
//with the method you provided in this case Car constructor with size of N
Yes, array size is not part of its type in Kotlin and there's no way to make it a part. This isn't specific to arrays; types can't depend on values in any way in Kotlin.
Size initialization in the type allow checking bound violation in runtime.
Array bounds are always checked at runtime on JVM. Even if a compiler wanted not to do it, it can't.
For your example, with the Butclass, you could use:
var arrayC: Array<But?> = arrayOfNulls(2) // no initialization required
or:
var arrayC: Array<But> = Array<But>(2){But()} // initialization required
But either way will not forbbid you of creating a new instance of a bigger array and assign it to the variable.
EDIT
The way I see it, there are two approaches to solve this.
The first would be to declare your array property as a var and test the assignement in your setter:
class Test {
var array: Array<Int> = Array(3){0}
set(value) {
if(value.size > 3)
throw IllegalArgumentException("The array size cannot be bigger than 3")
field = value
}
}
fun main(args: Array<String>) {
val test = Test()
test.array = arrayOf(0, 1, 2) // Ok
test.array = arrayOf(0, 1, 2, 3) // throws IllegalArgumentException
}
Or, if you want to deal with it at compile time, you can make your property final and initialize it with the size you want.
class Test {
val array: Array<Int> = Array(3){0}
}
fun main(args: Array<String>) {
val test = Test()
for (i in 0..2) // Ok
test.array[i] = i
for (i in 0..3) // throws ArrayIndexOutOfBoundsException
test.array[i] = i
test.array = arrayOf(0, 1, 2, 3) // compile time error: Val cannot be reassigned
}

Multidimensional array of Objects in Kotlin

I'm new in Kotlin, and I want to create a multi dimensional array of a custom class, with null permitted. Something like that
private var array_map = arrayOf<Array<Obstacle?>>()
...
array_map[1][2] = Obstacle()
How can I do it? Thank you!
In case you need the index of each element in the constructor of the elements of the array:
Declaration:
var matrix: Array<Array<Obstacle?>>
Instantiation and initialization:
matrix = Array(numRows) { row ->
Array(numCols) { col ->
Obstacle(row, col)
}
}
You can use private var arrayMap: Array<Array<Obstacle?>> = arrayOf(). Just wrap with as much Array<> as you need.
Not sure if this is what you want, but imagine that Obstacle is a custom class with a field num as below
data class Obstacle(var num: Int){}
A 2D array of the Obstacle object would be as below:
val array: Array<Obstacle?> = arrayOf(Obstacle(123), Obstacle(234))
val arrayOfArray: Array<Array<Obstacle?>> = arrayOf(array)
println(arrayOfArray[0][0]) // would print Obstacle(num=123)
println(arrayOfArray[0][1]) // would print Obstacle(num=234)
So you should be declaring your 2D array as below
val arrayOfArray: Array<Array<Obstacle?>> = arrayOf()
Your code will compile as is. The problem is just that array size can't be changed and arrayOf<Array<Obstacle?>>() creates an empty array, so array_map[1][2] = Obstacle() fails at runtime. (Unless you do array_map = ... somewhere between them. Note that you should prefer val arrayMap, which can't be reassigned, unless you have a specific reason to use var.)
If you want your array to start with nulls, there is arrayOfNulls in the standard library, but it only creates a single-dimensional array, and what you really need is an array of arrays of nulls. You can write a helper function:
inline fun <reified T> matrixOfNulls(n: Int, m: Int) = Array(n) { arrayOfNulls<T>(m) }
private val arrayMap = matrixOfNulls<Obstacle>(5, 5) // example arguments
The approach I always use for this case is:
arr2D = Array(sizeA) { Array(sizeB) { content } }
Note I replaced the sizes by fields names to illustrate that each number/field represents the width and height length of each dimension of the 2D array.
Also, content should be replaced by the main content you want to fill in each coordinate, in your case seems you aims to setup with Obstacle() instances. If you want fill this content in other moment put null or a quick Any() reference.
In this last case, after creating that you can simply perform to set the itens:
arr2D[1][2] = Obstacle()

How to create an array that has a set size but unset values?

How do I create an array, which has a set size, that is not known at compile time, but has unset values?
Essentially I want something like int immutable([length]);. length is not known at compile time. Obviously that doesn't compile though.
It'd have to be user-defined. The built-in arrays in D are either static, needed to be known at compile time, or slices into a dynamic array, which can be resized.
Built-in options:
int length = 100;
int[] int_array = new int[length]; // works, contents will be initialized to zero
That's the same as:
int[] int_array;
int_array.length = length;
You can also do immutable(int)[] if you want that, though then you won't be able to set the contents... the normal way to do this would be to write a pure function that creates and sets contents in a mutable array, then returns it:
pure int[] make_array(int length) {
int[] array;
array.length = length;
foreach(i, ref item; array)
item = i; // initialize it to a simple count
return array;
}
// usage:
immutable(int)[] arr = make_array(100); // works
immutable(int[]) iarr = make_array(100); // also works
Mutable data returned from a pure function is the exception to the general prohibition of implicit casting to imuutable: since it comes from a pure function, the compiler knows it is a unique reference, and safe to treat as immutable upon request.
The difference between the first and second line of usage is the first one can be reassigned: arr = something_else[]; /* cool */ whereas the second one cannot be changed at all. No length change, no contents change, no reassignment.
Static arrays are an option, but the length needs to be known at compile time:
int[100] int_array = void; // works, contents uninitialized, non-resizable, but length must be known at compile time
One possible strategy there is to declare a big int_array_buffer, then set int_array = int_array_buffer[0 .. length]. Though, still, int_array will be resizable itself.
To get everything you want, it'll have to be a user-defined type. Something alone these lines could work:
struct MyArray(T) {
#disable this(); // disallow default construction; force a length
// this constructor takes a runtime length and allocates the backing
this(size_t length) { backing = new T[length]; }
private T[] backing = void; // actually holds the data, uninitialized
T[] opSlice() { return backing; } // allow easy conversion to a slice
alias opSlice this; // implicit conversion to slice
}
When passing it to a function, you can pass MyArray!int, or a plain int[]. Thanks to the alias this, it will implicitly convert to int[], and thanks to the D slicing rules, even if the slice is resized in that function, it won't affect your MyArray instance.
Let's look at some usage:
void main() {
int length = 100; // runtime
// MyArray!int uninitialized; // compile error thanks to #disable this
MyArray!int uninitialized = void; // ok, explicitly uninitialized
uninitialized = MyArray!int(length); // created
// or
auto my_array = MyArray!int(length); // also creates it
// my_array.length = 20; // compile error "my_array.opSlice().length is not an lvalue" - it is not resizable
// my_array ~= 1; // compile again, "cannot append type int to type MyArray!int" - again because it is not resizable
int[] slice = my_array; // works, makes passing it to functions that expect normal arrays easy
}
That should give you everything you need. With wrapper structs in D, you can selectively enable and disable functionality of the underlying type with no loss in efficiency.
No need for a wrapper (unless I misunderstood). Just use the allocation primitives in http://dlang.org/phobos/core_memory.html. You can get a chunk of memory that's essentially an uninitialized array of anything you wish.

Resources