class Foo() {
val array // how do I leave initialization till later?
def initializeArray(size : Int) = array = Array.ofDim[Int](size)
}
The above code won't compile, so how do I initialize my array at a later time?
Edit
Suppose I need to read a file that has a matrix of integer, and I want to represent the matrix as a two dimensional array. Of course, I am parsing the file inside the Foo class and the dimension of the matrix won't be known until the parsing is done.
You could use a lazy val e.g.
scala> class Foo {
| lazy val array = initialize
| def initialize = {
| println("initializing...")
| new Array[Int](5)
| }
| }
defined class Foo
scala> val f = new Foo
f: Foo = Foo#135810c
scala> f.array
initializing...
res46: Array[Int] = Array(0, 0, 0, 0, 0)
Now you have deferred when the array is initialized.
One simple way would be to just initialize it to null. To do this you would need to specify a type, Array[Int] and to make it a var (instead of val) so that you could change it later:
class Foo() {
var array: Array[Int] = null // how do I leave initialization till later?
def initializeArray(size : Int) = array = Array.ofDim[Int](size)
}
However, this is not very good practice in Scala. It might be better to use an Option:
class Foo() {
var array: Option[Array[Int]] = None // how do I leave initialization till later?
def initializeArray(size : Int) = array = Some(Array.ofDim[Int](size))
}
This tells a user, explicitly, that it is possible that array may not be set to anything, and avoids NullPointerExceptions. You can read more about Option on StackOverflow or elsewhere.
Finally, the best designs in Scala rely on immutable classes. In such a case, you would defer creation of the Foo until you actually know what you want to put in it. However, without knowing anything else about your design, it's hard to say how best to set this up.
EDIT: Based on your description, I would separate your design into two parts: a method for parsing the file, and an immutable Foo for storing the final result.
class Foo(array: Array[Int]) {
}
object Foo {
def fromFile() = {
val array: Array[Int] = /* parse file into the right structure */
new Foo(array)
}
}
Then you could just say:
val foo = Foo.fromFile(filename)
and the foo would be an complete, immutable Foo.
Related
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
}
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()
I am trying to create an array of Char using another array of Int's size. Code doesn't compile:
object Main {
def main(args: Array[String]): Unit = {
val mapping = Map(1 -> "ABC", 2 -> "DEF")
val a = mapping.keySet.toArray
val c = Array[Char](a.length)
}
}
The compiler throws an error: "type mismatch; found : Int required: Char"
when I change the code above to:
val c = Array[Char](2) // no compiler error
Looks like the compiler is interpreting my input not as a size param but instead thinking it's a Char such as an initial element of the Char array
Since in java this code would compile without a problem I was wondering
what's the proper way of using another array length as a size param to init a different array in Scala?
You should be using .ofDim in your last line
val c = Array.ofDim[Char](a.length)
The second one works
val c = Array[Char](2)
as the compiler is treating 2 itself as a character.
Array type in scala has one confusing aspect, let me help you clarify it:
1.Array type has a class and a object, the object is called companion object for the specific class.
2.object Array has an apply method which in the code you use, but it can not be construct the same as the companion class.
To this snippet of code, the solution is:
object Main {
def main(args: Array[String]): Unit = {
val mapping = Map(1 -> "ABC", 2 -> "DEF")
val a = mapping.keySet.toArray
val c = new Array[Char](a.length)
}
}
Please be careful about the change of it, add new keyword to the created Array class.
I want to pass an array to an object and store a reference to this array. I want to be able to modify this array within this object and make sure that it's modified everywhere else.
Here is what I am trying to accomplish (how the code doesn't work)
class Foo {
var foo : Array<Int>
init(foo: Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
var a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
print(a) // My goal is that it will print 1,2,3,4,5
My findings so far
A) The array (by default) are passed strange way. It's a reference until you modify an array length. As soon as you modify a length it will be copied and modified. As result, if I append or delete anything from it in the object it won't be seen outside
B) I can use inout on a function parameter. This will allow me to modify it within this function. However, as soon as I will try to assign it to some object member I am again struck by A)
C) I can wrap an array in some Container class. This probably is the cleanest way. However, I serialize/deserialize these objects and I would rather not put it in Container (because I will have to work around some things for serialization and deserialization and sending it to the server).
Are there anything else? Am I missing some Swift construct which allows me to do that?
You'll have to use an NSArray or NSMutableArray for this because Swift Arrays are value types so any assignment will make a copy.
You could make use of Swifts (very un-swifty) UnsafeMutablePointer.
Since (from your post) the behaviour references to arrays can't really seem be trusted, instead keep an UnsafeMutablePointer companion to the class inner array foo as well as any "external" arrays that you want to be binded to foo, in the sense that they are both just pointers to same address in memory.
class Foo {
var foo : [Int]
var pInner: UnsafeMutablePointer<Int>
init(foo: [Int]) {
pInner = UnsafeMutablePointer(foo)
self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count))
}
func modify(inout pOuter: UnsafeMutablePointer<Int>) {
foo.append(5) // <-- foo gets new memory adress
pInner = UnsafeMutablePointer(foo)
pOuter = pInner
}
}
var a = [1,2,3,4] // first alloc in memory
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a)
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a'
print(bar.foo) // [1,2,3,4]
bar.modify(&pOuter) // -> [1,2,3,4,5]
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count))
/* Same pointer adress, OK! */
print(bar.pInner)
print(pOuter)
/* Naturally same value (same address in memory) */
print(bar.foo)
print(a)
Pointers can be dangerous though (hence the fitting type name), and, again, very un-swifty. Anyway...
/* When you're done: clear pointers. Usually when using
pointers like these you should take care to .destroy
and .dealloc, but here your pointers are just companions
to an Array property (which has a pointer an reference
counter itself), and the latter will take care of the
objects in memory when it goes out of scope. */
bar.pInner = nil
pOuter = nil
Now, what happens when either a or foo goes out of scope, will it break the variable that are not out of scope, or does Swift contain some clever reference counting that realises a memory address is still in use? I haven't investigated this, but feel free to indulge yourself in that.
From the Swift Programming Language,
Structures are always copied when they are passed around in your code, and do not use reference counting.
If you examine the contents of the array variable, you will see that indeed the append works:
class Foo {
var foo : Array
init(_ foo: Array) {
self.foo = foo
}
func modify() {
foo.append(5)
}
func printFoo() {
print("self.foo: \(foo)")
}
}
let a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
bar.printFoo()
print("a: \(a)")
produces
self.foo: [1, 2, 3, 4, 5]
a: [1, 2, 3, 4]
You have taken a copy of a, not a reference to a.
a is declared a constant hence cannot be modified. If you are planning to modify the contents of a, declare it as a variable. i.e.,
var a = [1,2,3,4]
I haven't tested this but, as you are using a class to wrap the array, I see no reason why the following would not work.
class Foo {
var foo : Array<Int>
init(foo: inout Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
let a = [1,2,3,4]
let bar = Foo(&a)
bar.modify()
print("a: \(a)") // a: [1,2,3,4,5]
How would I make an exact duplicate of an array?
I am having hard time finding information about duplicating an array in Swift.
I tried using .copy()
var originalArray = [1, 2, 3, 4]
var duplicateArray = originalArray.copy()
Arrays have full value semantics in Swift, so there's no need for anything fancy.
var duplicateArray = originalArray is all you need.
If the contents of your array are a reference type, then yes, this will only copy the pointers to your objects. To perform a deep copy of the contents, you would instead use map and perform a copy of each instance. For Foundation classes that conform to the NSCopying protocol, you can use the copy() method:
let x = [NSMutableArray(), NSMutableArray(), NSMutableArray()]
let y = x
let z = x.map { $0.copy() }
x[0] === y[0] // true
x[0] === z[0] // false
Note that there are pitfalls here that Swift's value semantics are working to protect you from—for example, since NSArray represents an immutable array, its copy method just returns a reference to itself, so the test above would yield unexpected results.
There is a third option to Nate's answer:
let z = x.map { $0 } // different array with same objects
* EDITED * edit starts here
Above is essentially the same as below and actually using the equality operator below will perform better since the array won't be copied unless it is changed (this is by design).
let z = x
Read more here: https://developer.apple.com/swift/blog/?id=10
* EDITED * edit ends here
adding or removing to this array won't affect the original array. However, changing any of the objects' any properties that the array holds would be seen in the original array. Because the objects in the array are not copies (assuming the array hold objects, not primitive numbers).
Nate is correct. If you are working with primitive arrays all you need to do is assign duplicateArray to the originalArray.
For the sake of completeness, if you were working an NSArray object, you would do the following to do a full copy of an NSArray:
var originalArray = [1, 2, 3, 4] as NSArray
var duplicateArray = NSArray(array:originalArray, copyItems: true)
For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
And then the Array extension for cloning:
extension Array where Element: Copying {
func clone() -> Array {
var copiedArray = Array<Element>()
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
and that is pretty much it, to view code and a sample check this gist
If you want to copy the items of an array of some class object.
Then you can follow the below code without using NSCopying protocol but you need to have an init method which should take all the parameters that are required for your object.
Here is the code for an example to test on playground.
class ABC {
var a = 0
func myCopy() -> ABC {
return ABC(value: self.a)
}
init(value: Int) {
self.a = value
}
}
var arrayA: [ABC] = [ABC(value: 1)]
var arrayB: [ABC] = arrayA.map { $0.myCopy() }
arrayB.first?.a = 2
print(arrayA.first?.a)//Prints 1
print(arrayB.first?.a)//Prints 2