Reputation: 1892
I have two lists in Kotlin, of the same size, foodObjects: MutableList<ParseObject>?
and checked: MutableList<Boolean>?
. I need to do a for
loop and get the objectId
from foodObjects
every time that an element of checked
is true. So it is this in Java:
for (int i = 0; i < foodObjects.size(); i++) {
// here
}
But in Kotlin, I don't know why, there are some problems. In fact, if I do this:
for (i in 0..foodObjects!!.size) {
if (checked?.get(i) == true) {
objectsId?.add(foodObjects.get(i).objectId)
}
}
I've got IndexOutOfBoundsException
. I don't know why, it continues the loop also at foodObjects.size
. I could do it also with filter
and map
:
(0..foodObjects!!.size)
.filter { checked?.get(it) == true }
.forEach { objectsId?.add(foodObjects.get(it).objectId) }
but I'm getting the same error. I use this to stop the error and get it to work:
for (i in 0..foodObjects!!.size) {
if (i < foodObjects.size) {
if (checked?.get(i) == true) {
objectsId?.add(foodObjects.get(i).objectId)
}
}
}
Everyone could tell me why in Kotlin I need to do it, when in Java it works good?
Upvotes: 3
Views: 10151
Reputation: 24532
Here are various ways to ensure the index is valid:
if (index in myList.indices) {
// index is valid
}
// The rangeUntil operator (..<) is still exprimental in Kotlin 1.7.20
if (index in 0..<myList.size) {
// index is valid
}
if (index in 0 until myList.size) {
// index is valid
}
if (index in 0..myList.lastIndex) {
// index is valid
}
if (index >= 0 && index <= myList.lastIndex) {
// index is valid
}
// Note: elements of the list should be non-null
if (myList.getOrNull(index) != null) {
// index is valid
}
// Note: elements of the list should be non-null
myList.getOrNull(index)?.let { element ->
// index is valid; use the element
}
Upvotes: 0
Reputation: 29844
Take a look at this example from the Kotlin documentation for ranges:
if (i in 1..10) { // equivalent of 1 <= i && i <= 10
println(i)
}
As you can see
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
will be printed. So, the 10 is included.
The highest index of your collection foodObjects
is (foodObjects.size() - 1)
because it starts with 0.
So, to fix your problem, just do this:
for(i in 0..(foodObjects.size - 1)) {
// ...
}
A better way to write this would be:
for((i, element) in foodObjects.withIndex()){
// do something with element
println("The index is $i")
}
This way you have the element and the index at once and don't need to worry about ranges.
*I removed the null checks for simplicity.
Upvotes: 0
Reputation: 89578
Ranges in Kotlin are inclusive, therefore 0..foodObjects!!.size
starts at 0
and ends at foodObjects.size
, including both ends. This causes the exception when your loop attempts to index the list with its own size, which is one more than the largest valid index.
To create a range that doesn't include the upper bound (like your Java loop), you can use until
:
for(i in 0 until foodObjects!!.size) {
// ...
}
You could also clean your code up a bit if you did null checks on the collections you're using up front:
if (foodObjects != null && checked != null && objectsId != null) {
for (i in 0 until foodObjects.size) {
if (checked.get(i) == true) {
objectsId.add(foodObjects.get(i).objectId)
}
}
}
else {
// handle the case when one of the lists is null
}
And to get rid of having to handle indexes altogether, you can use the indices
property of a list (plus I use the indexing operator here instead of get
calls):
for (i in foodObjects.indices) {
if (checked[i]) {
objectsId.add(foodObjects[i].objectId)
}
}
You could also use forEachIndexed
:
foodObjects.forEachIndexed { i, foodObject ->
if (checked[i]) {
objectsId.add(foodObject.objectId)
}
}
Upvotes: 12