Property Set of same Class throws Exception StackOverflow in Kotlin - kotlin

The problem goes away when instead of data class I use simply a class or instead of a MutableSet I use an ArrayList. I am trying to understand what is wrong in the way I first wrote it and can't find anything wrong, but it throws a StackOverflow error when running the last line
data class Person(
val id : Int,
val neighbors : MutableSet<Person> = mutableSetOf()
)
fun main() {
val neighbor1 = Person(0)
val commonNeighbor = Person(1)
val neighbor2 = Person(2)
commonNeighbor.neighbors.addAll(listOf(neighbor1,neighbor2))
neighbor1.neighbors.add(commonNeighbor)
neighbor2.neighbors.add(commonNeighbor)
}

(Assuming you are using Kotlin/JVM)
The stack trace makes this a lot clearer. Notice that the stack trace is a repetition of calling these two methods.
at bar.Person.hashCode
at java.base/java.util.AbstractSet.hashCode
Let's consider what happens when you add commonNeighbor into neighbour2.neighbours in the final line.
mutableSetOf creates a linked hash set, and in order to be added to a hash set, commonNeighbour needs to be hashed.
An implementation of hashCode is automatically generated for Person because it is a data class, and this is what is used to hash it. This implementation takes into account (i.e. also calls hashCode on) the properties declared in Person's primary constructor, which are id and neighbours.
So to hash commonNeighbours, we need to hash commonNeighbours.id and commonNeighbours.neighbours. What neighbours do commonHeighbours have? neighbour1 and neighbour2. In order to hash them, we need to hash their neighbours too. But wait! One of neighbour1's neighbours is commonNeighbours! We have gone back to where we have started, creating an infinite loop.
All of this wouldn't happen, if the hashCode of Person didn't consider neighbours (which is what making Person a non-data class would do), or if you weren't using a HashSet.

Related

Kotlin modifying dataclass object key from map changes the reference after modifying variable

I have a MutableMap that its keys are objects from a DataClass (User dataclass), and the values are arrays from other Dataclass (Dog dataclass). If i have a variable with a User object, and i put it in the MutableMap and i test if the map contains the User, it says that is true. But after putting the user in the MutableMap if i change one of the attributes of the User object using the variable that holds the User object, the Map says that it doesnt contains the user object.
This is an example
data class User(
var name: String,
var project: String,
)
data class Dog(
var kind: String
)
fun main(args: Array<String>) {
var mapUserDogs: MutableMap<User, MutableList<Dog>> = mutableMapOf()
var userSelected = User("name2", "P2")
mapUserDogs.put(
User("name1", "P1"),
mutableListOf(Dog("R1"), Dog("R2"))
)
mapUserDogs.put(
userSelected,
mutableListOf(Dog("R21"), Dog("R31"))
)
println(userSelected)
println(mapUserDogs.keys.toString())
println(mapUserDogs.contains(userSelected))
println(mapUserDogs.values.toString())
println("\n")
userSelected.name = "Name3"
println(userSelected)
println(mapUserDogs.keys.toString())
println(mapUserDogs.contains(userSelected))
println(mapUserDogs.values.toString())
}
The prints statements show this:
User(name=name2, project=P2)
[User(name=name1, project=P1), User(name=name2, project=P2)]
true
[[Dog(kind=R1), Dog(kind=R2)], [Dog(kind=R21), Dog(kind=R31)]]
User(name=Name3, project=P2)
[User(name=name1, project=P1), User(name=Name3, project=P2)]
false
[[Dog(kind=R1), Dog(kind=R2)], [Dog(kind=R21), Dog(kind=R31)]]
Process finished with exit code 0
But it doesn't make sense. Why the map says that it doesn't contains the user object if its clear that it still holds the reference to it after being modified?
User(name=Name3, project=P2)
[User(name=name1, project=P1), User(name=Name3, project=P2)]
The user in the keys collection was also changed when i modified the userSelected variable, so now the object has it attribute name as "Name3" in both the variable and in the Map keys, but it still says that it doesnt contains it.
What can i do so that i can change the attributes in the userSelected object and the Map still return true when using the "contains" method?. And doing the same process in reverse shows the same. If i get from the map the user and i modify it, the userVariable is also modified but if i later test if the map contains the userVariable, it says false.
What can i do so that i can change the attributes in the userSelected object and the Map still return true when using the "contains" method?
There is nothing you can do that preserves both your ability to look up the entry in the map and your ability to modify the key.
Make your data class immutable (val instead of var, etc.), and when you need to change a mapping, remove the old key and put in the new key. That's really the only useful thing you can do.
To add to Louis Wasserman's correct answer:
This is simply the way that maps work in Kotlin: their contract requires that keys don't change significantly once stored. The docs for java.util.Map* spell this out:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
The safest approach is to use only immutable objects as keys. (Note that not just the object itself, but any objects it references, and so on, must all be immutable for it to be completely safe.)
You can get away with mutable keys as long as, once the key is stored in the map, you're careful never to change anything that would affect the results of calling equals() on it. (This may be appropriate if the object needs some initial set-up that can't all be done in its constructor, or to avoid having both mutable and immutable variants of a class.) But it's not easy to guarantee, and leaves potential problems for future maintenance, so full immutability is preferable.
The effects of mutating keys can be obvious or subtle. As OP noticed, mappings may appear to vanish, and maybe later reappear. But depending on the exact map implementation, it may cause further problems such as errors when fetching/adding/removing unrelated mappings, memory leaks, or even infinite loops. (“The behaviour… is not specified” means that anything can happen!)
What can i do so that i can change the attributes in the userSelected object and the Map still return true when using the "contains" method?
What you're trying to do there is to change the mapping. If you store a map from key K1 to value V, and you mutate the key to hold K2, then you're effectively saying “K1 no longer maps to V; instead, K2 now maps to V.”
So the correct way to do that is to remove the old mapping, and then add the new one. If the key is immutable, that's what you have to do — but even if the key is mutable, you must remove the old mapping before changing it, and then add a new mapping after changing it, so that it never changes while it's stored in the map.
(* The Kotlin library docs don't address this, unfortunately — IMHO this is one of many areas in which they're lacking, as compared to the exemplary Java docs…)
That happens because data classes in Kotlin are compared by value, unlike regular classes which are compared by reference. When you use a data class as a key, the map gets searched for a User with the same string values for the name and project fields, not for the object itself in memory.
For example:
data class User(
var name: String,
var project: String,
)
val user1 = User("Daniel", "Something Cool")
val user2 = User("Daniel", "Something Cool")
println(user1 == user2) // true
works because, even though they are different objects (and thus different references), they have the same name and project values.
However, if I were to do this:
user1.name = "Christian"
println(user1 == user2) // false
the answer would be false because they don't share the same value for all of their fields.
If I made User a standard class:
class User(
var name: String,
var project: String,
)
val user1 = User("Daniel", "Something Cool")
val user2 = User("Daniel", "Something Cool")
println(user1 == user2) // false
the answer would be false because they are different references, even though they share the same values.
For your code to work the way you want, make User a regular class instead of a data class.
That's the key difference between regular classes and data classes: a class is passed by reference, a data class is passed by value. Data classes are nothing more than collections of values with (optionally) some methods attached to them, classes are individual objects.

How to get last Key or Value in Kotlin Map collection

How can I get the last key or value in a Kotlin Map collection? It seems like it cannot be done by using an index value.
There's a couple ways it can be done. While you can't elegantly print a map directly, you may print it's entry set.
The first way, and the way that I DO NOT recommend, is by calling the .last() function on the entry set. This can be accomplished with testMap.entries.last(). The reason I don't recommend this method is because in real data this method is non-deterministic -- meaning there's no way to guarantee the characteristics of the value returned.
While I don't recommend this method, I don't know your application and this may be sufficient.
I DO recommend using the .sortedBy() function on your entry set, and then calling .last() on it. This allows you to make some sort of assumption about the results returned, something that is typically necessary, otherwise why do you want the last?
See this example comparing the two methods and then comparing the method against the order you would get if you iterate with the .forEach function:
fun main(args: Array<String>) {
val testMap = mutableMapOf<Long, String>()
testMap[1] = "Hello"
testMap[5] = "World"
testMap[3] = "Foobar"
println(testMap.entries.last())
println(testMap.entries.sortedBy { it.key }.last())
println("\norder via loop:")
testMap
.entries
.forEach {
println("\t$it")
}
}
Take a look at the output:
3=Foobar
5=World
order via loop:
1=Hello
5=World
3=Foobar
Here we see that the value returned from .last(), is the last value that was inserted into the map - the same happens with .forEach. This is okay, but usually we want our map to have some sort of order. In this example, i've called for the entry set to be sorted by the key value, so that our call to .last() on the entry set returns the key/value pair with the largest key.

Is true way use function for create instance and use it?

To Attention DRY principle, I want to know is true define a function to return instance of class or no!
You suppose we have a class and create instance many times in code, Is it better create instance it any time or define a function and use it?
class Job(name:String, maxPool:Int)
val job = Job("JOB_1",100)
val job2 = Job("JOB_2",100)
val job3 = Job("JOB_3",200)
or define a function
fun newJob(name:String = "NEW_JOB", maxPool:Int = 100) : Job{
Job(name, maxPool)
}
val job = newJob()
val job2 = newJob("JOB_2")
val job3 = newJob("JOB_3")
What you have there is a factory function.  And because it's closely coupled to the class, it's best to keep it in (or near) the class; that way it's easier to find, less likely to get out-of-date, etc.
If the function isn't doing any significant processing before creating the instance, just call the constructor directly (as per your first example).
Or if the function is merely inserting a constant value for some parameters, then give the constructor those default argument values.
Or if the function is converting parameters to different types, or other simple processing, then use a secondary constructor which does the conversion before calling the primary constructor.
Or if the function is doing more complex processing, then put the factory function in the class's companion object.  (If you call it operator fun invoke(), then it will look like a constructor call — though it's only worth doing that if the parameter types make its meaning obvious; otherwise, give it a meaningful name.)
Only if the function is doing some processing that doesn't belong in the class at all — e.g. it's implementing some business logic that the class doesn't need to know about — is it worth having a separate factory function as in your second example.

Why can't object refer to parent object and child object?

Look, I'm facing this basic problem where I want to construct sort of nodes where node is a category and it could have parent category and list of it's children categories, I just realized that it's impossible in OOP or am I wrong? I can handle my case in a way that each object will have only list of its children but then I'll have to (in my opinion) unnecessarily filter through all of the categories to find it's parent here is a basic idea of problem I'm facing
Why is this happening?
All your constructors require the parent and child to be constructed before the current object can be constructed. That is impossible in your case as the references form a dependency cycle (First -> Fourth -> Third -> Second -> First, therefore First can't be fully created till after First is created, etc.).
One solution would be to make the Node abstract (or an interface) and only fetch the values when they are requested (i.e. in the getters):
interface Node {
val number: Int
val parent: Node?
val child: Node?
}
object First : Node {
override val number = 1
override val parent get() = Fourth
override val child get() = Second
}
// Similar for Second, Third, and Fourth
If you're interested in why you got that particular pattern of nulls, it's important to remember that objects in Kotlin usually aren't created till the first time they are used.
The first object you use (in main) is First, so it's constructor is called.
The first* thing its constructor does is get a reference to Fourth, which in turn does Third, which in turn does Second, which in turn tries to do First, but First is already in the process of being created, so its reference is null.
Then (we're still in Second) it tries to create Third (as the child), which is in the process of being created, so its reference is also null. At this point Second is done, so any future reference to it will not be null.
Third now has a valid reference to Second, and moves on to trying to create Fourth, which is still being created, thus null. Now Third is done.
Fourth now has a valid reference to Third, and moves on to First, which is still being created, thus null. Now Forth is done.
First now has a valid reference to Fourth, and moves on to Second, which already exists and is valid. Now First is done.
*Technically the second, since the first thing it does is set the number, but it's the first thing that matters for the purposes of this discussion.

Recursively building a data class in Kotlin

I have am trying to create a recursive data class like so:
data class AttributeId (
val name: String,
val id: Int,
val children: List<AttributeId>?
)
The thing I'm struggling with now is building the data class by iterating over a source object.
How do I recursively build this object?? Is a data class the wrong solution here?
EDIT: Some more information about the Source object from which I want to construct my data class instance
The source object is a Java Stream that essentially* has the following shape:
public Category(final String value,
final Integer id,
final List<Category> children) {
this.value = value;
this.id = id;
this.children = children;
}
(For brevity the fields I don't care about have been removed from example)
I think I need to map over this stream and call a recursive function in order to construct the AttributeId data class, but my attempts seem to end in a stack overflow and a lot of confusion!
I don't think there's anything necessarily wrong with a data class that contains references to others.
There are certainly some gotchas.  For example:
If the list were mutable, or if its field was mutable (i.e. var rather than val), then you'd have to take care because its hashcode &c could change.
And if the chain of links could form a loop (i.e. you could follow the links and end up back at the original class), that could be very dangerous.  (E.g. calling a method such as toString() or hashCode() might either get stuck in an endless loop or crash the thread with a StackOverflowError.  You'd have to prevent that by overriding those methods to prevent them recursing.)  But that couldn't happen if the list and field were both immutable.
None of these issues are specific to data classes, though; a normal class could suffer the same issues (especially if you overrode methods like toString() or hashCode() without taking care).  So whether you make this a data class comes down to whether it feels like one: whether its primary purpose is to hold data, and/or whether the automatically-generated methods match how you want it to behave.
As Tenfour04 says, it depends what you're constructing these from.  If it naturally forms a tree structure, then this could be a good representation for it.
Obviously, you wouldn't be able to construct a parent before any of its children.  (In particular, the first instance you create would have to have either null or an empty list for its children.)  This would probably mean traversing the source in post-order.  The rest should fall out naturally from that.