For loop while removing items from ArrayList - kotlin

I am making a game in Kotlin.Using Libgdx and Box2d
1.function creates bodies via Box2d and adds these bodies in bodyArrayList:ArrayList
2.function creates textures and adds these textures in textureArrayList
3.function is matching items of bodyArrayList with items of textureArrayList. The function has for loop.
4.function is removing bodies when crash a wall.
5.function is removing textures when crash a wall.
All functions are working in update() method.
All these are working perfectly. However because I cannot remove items of te ArrayLists while for loop works on these ArrayLists, items of ArrayList is increasing and the game is starting slowing downn 1 hour later. The ArrayLists' size is going up as you guess.
If I try to remove items of ArrayLists I am taking exceptions in matching() function as you guess.
My question is how can I remove these items from ArrayLists while "for loop" is working in a safe manner.
1.function creates bodies
var body = makeBody()
bodyArray.add(body)
2.function creates textures
var texture=makeTexture()
textureArray.add(texture)
3.function is matching items of bodyArrayList with items of textureArrayList
for (i in 0..bodyArray.size-1) {
textureArray[i].setPosition(bodyArray[i].position.x, bodyArray[i].position.y)
}
4.funtion is removing bodies when crash a wall.
override fun beginContact(contact: Contact?) {
val userDataA = contact!!.fixtureA.userData.toString()
val userDataB = contact.fixtureB.userData.toString()
if ((userDataA == "ball" && userDataB == "wall")){
val bodyA=contact.fixtureA.body
Gdx.app.postRunnable(Runnable(){
run(){
world.destroyBody(bodyA) } })
}
5.function is removing textures when crash a wall.
for (texture: MyTextures in MyTextures.getList(Ball::class.java.canonicalName)) {
if (ball.overlaps(wall)) {
ball.remove() }
}

You can use Iterator to remove items from Collections while iterating
val list = arrayListOf<Int>()
val listIterator = list.iterator()
while(listIterator.hasNext()){
//get item
val item = listIterator.next()
//items can be removed safely using
listIterator.remove()
}

if you are using badLogic Array class you can give your texture class to it and dispose every item of it in dispose method
private val texture = Texture("badlogic.jpg")
private val myArray = Array<Texture>()
fun dispose(){
myArray[i].dispose()
}
this is the link for more info on textures...
also you can dispose Screen Class

Related

Thread-safe access to the same variable from different flows (Kotlin)

Is this code thread safe? Do I need a synchronized block or something like that? source1 and source2 endless Kotlin Flow
viewModelScope.launch {
var listAll = mutableListOf<String>()
var list1 = mutableListOf<String>()
var list2 = mutableListOf<String>()
launch {
source1.getNames().collect { list ->
list1 = list
listAll = mutableListOf()
listAll.addAll(list1)
listAll.addAll(list2)
//then consume listAll as StateFlow or return another flow with emit(listAll)
}
}
launch {
source2.getNames().collect { list ->
list2 = list
listAll = mutableListOf()
listAll.addAll(list2)
listAll.addAll(list1)
//then consume listAll as StateFlow or return another flow with emit(listAll)
}
}
}
This code is not thread safe.
However, it is called from viewModelScope.launch which runs on Dispatchers.Main by default. So your inner launch blocks will be called sequentially. This means that after all you will get the result which is produced by second launch block.
To achieve asynchronous behavior, you want to use viewModelScope.launch(Dispatchers.Default).
Your code will probably fire concurrent modification exception in that case.
To synchronize it, you may want to use Java's Collections.synchronizedList which blocks the list while one thread is performing operations with it, so the other thread are not able to perform modifications.
Or perform synchronizing manually using Mutex.
val mutex = Mutex()
viewModelScope.launch(Dispatchers.Default) {
launch {
mutex.withLock {
... // Your code
}
}
launch {
mutex.withLock {
... // Your code
}
}
}
Read official Kotlin guide to shared mutable state
After all, I am struggling to imagine real life example in which you will actually use that code. You probably don't need asynchronous behavior, you will be fine without using two launch blocks. Or you should rethink your design to avoid need of manual synchronization of two coroutines.

MutableList of MutableLists in Kotlin: adding element error

Why I'm getting "java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0" while running next code??? :
val totalList = mutableListOf<MutableList<Int>>()
fun main() {
for (i in 0..15) {
for (j in 0..10) {
*some operations and calculations with **var element of type Int***
totalList[i].add(element)
}
}
}
I was thinking that in such case while iterating through 'j' it should add elements to mutableList[i], after this it should start adding elements to mutableList[i + 1] etc.... But instead I am recieving IndexOutOfBoundsException....
val totalList = mutableListOf<MutableList<Int>>()
All this does is create one list which is going to contain MutableList<Int> items. Right now, there's nothing in it (you've supplied no initial elements in the parentheses).
Skip forward a bit, and you do this:
totalList[0].add(element)
You're trying to get the first element of that empty list and add to it. But there is no first element (index 0) because the list is empty (length 0). That's what the error is telling you.
There's lots of ways to handle this - one thing you could do is create your lists up-front:
// create the 16 list items you want to access in the loop
// (the number is the item count, the lambda generates each item)
val totalList = MutableList(16) { mutableListOf<Int>() }
// then refer to that list's properties in your loop (no hardcoded 0..15)
for (i in totalList.indices) {
...
// guaranteed to exist since i is generated from the list's indices
totalList[i].add(element)
}
Or you could do it the way you are now, only using getOrElse to generate the empty list on-demand, when you try to get it but it doesn't exist:
for (i in 0..15) {
for (j in 0..10) {
// if the element at i doesn't exist, create a list instead, but also
// add it to the main list (see below)
totalList.getOrElse(i) {
mutableListOf<Int>().also { totalList.add(it) }
}.add(element)
}
}
Personally I don't really like this, you're using explicit indices but you're adding new list items to the end of the main list. That implicity requires that you're iterating over the list items in order - which you are here, but there's nothing enforcing that. If the order ever changed, it would break.
I'd prefer the first approach - create your structure of lists in advance, then iterate over those and fill them as necessary. Or you might want to consider arrays instead, since you have a fixed collection size you're "completing" by adding items to specific indices
Another approach (that I mentioned in the comments) is to create each list as a whole, complete thing, and then add that to your main list. This is generally how you do things in Kotlin - the standard library contains a lot of functional tools to allow you to chain operations together, transform things, and create immutable collections (which are safer and more explicit about whether they're meant to be changed or they're a fixed set of data).
for (i in 0..15) {
// map transforms each element of the range (each number) to an item,
// resulting in a list of items
val items = (0..10).map { j ->
// do whatever you're doing
// the last expression in the lambda is its resulting value,
// i.e. the item that ends up in the list
element
}
// now you have a complete list of items, add them to totalList
totalList.add(items)
}
(Or you could create the list directly with List(11) { j -> ... } but this is a more general example of transforming a bunch of things to a bunch of other things)
That example there is kinda half and half - you still have the imperative for loop going on as well. Writing it all using the same approach, you can get:
val totalList = (0..15).map { i ->
(0..10).map { j ->
// do stuff
element
}
}
I'd probably prefer the List(count) { i -> ... } approach for this, it's a better fit (this is a general example). That would also be better since you could use MutableList instead of List, if you really need them to be mutable (with the maps you could just chain .toMutableList() after the mapping function, as another step in the chain). Generally in Kotlin, collections are immutable by default, and this kind of approach is how you build them up without having to create a mutable list etc. and add items to it yourself

Why add method overwrites all items in array list

I have board for game tic-tac-toe. When I'm tryna add new board to list of boards it's always overwrites
fun String.replaceUnusualSymbols() =
replace(", ", "").replace("[", "").replace("]", "")
fun Array<Array<Char>>.printBoard(board: Array<Array<Char>>) = indices.forEach {
println("|${board[it].contentToString().replaceUnusualSymbols()}|")
}
fun main() {
var board = Array(3) { Array(3) { ' ' } }
val game: ArrayList<Array<Array<Char>>> = arrayListOf()
board[0][0] = 'X'
game.add(board) // add first board
board[1][1] = 'X'
game.add(board) // add second board
println(game.size)
game.forEach {
it.printBoard(board) // but outputs always second board
}
}
How to fix this?
There are two problems.
Array is a mutable collection. Since you only created one var board, there's only once instance - which is mutated every time you do board[0][0] = 'X' or board[0][1] = '0'.
it.printBoard(board) isn't printing each board, it's only printing the same board.
Copying the mutable Array
You can try copying the board every time you want to add the current board state to game.
Note that because board is a nested array, just doing board.copyOf() isn't enough - this won't copy recursively. So we have to specifically copy each row of board as well.
Because map {} returns a List and board is an Array, we have to convert the copied result using toTypedArray().
board[0][0] = 'X'
game.add(board.map { row -> row.copyOf() }.toTypedArray()) // add first board
board[1][1] = '0'
game.add(board.map { row -> row.copyOf() }.toTypedArray()) // add second board
What we end up with is something that's pretty clunky and verbose. We can try making an extension function for the copy, but then we'll still have the risk involved with mutable collections. What happens if at some point you forget to copy the board?
To keep this answer short and focused, I'll leave refactoring to use immutable collections as an exercise for the reader, with some hints:
How can you use a class to encapsulate the game logic?
Instead of using a List<List<Char>>, can you create a data class?
Can you create a function that both 1. adds a new symbol to the board, and 2. saves the current game state, in one go?
printBoard()
printBoard() is an extension function has both a receiver and an argument of type Array<Array<Char>>, which is unusual.
// receiver
// ↓
fun Array<Array<Char>>.printBoard(board: Array<Array<Char>>)
// ↑
// argument
This is causing one bug. When you call it.printBoard(board), it will use the indices of it, but it will print the values of board.
// the indices come from the receiver
// ↓
fun Array<Array<Char>>.printBoard(board: Array<Array<Char>>) = indices.forEach {
println("|${board[it].contentToString().replaceUnusualSymbols()}|")
} // ↑
// the values come from the argument
So when you use it in a loop like this
game.forEach {
// argument
// ↓
it.printBoard(board)
// ↑
// receiver
}
it won't print each board.
Here's a fixed version that will only print the receiver.
fun Array<Array<Char>>.printBoard() = indices.forEach {
println("|${this[it].contentToString().replaceUnusualSymbols()}|")
}
Replace replaceUnusualSymbols() with joinToString()
And I'll go one step further - we can get rid of contentToString() and replaceUnusualSymbols() and use the Kotlin stdlib function joinToString():
fun Array<Array<Char>>.printBoard() {
val boardString = joinToString(
separator = "\n",
prefix = "\n",
) { row ->
row.joinToString(" ", prefix = "|", postfix = "|")
}
println(boardString)
}

How to correctly update value of IntegerProperty in the view?

I am building simple application that will track a certain tabletop game of 2 players.
I have a view called MatchView
class MatchView : View() {
// data
private var currentRound = SimpleIntegerProperty(0)
private var currentTurn = SimpleIntegerProperty(0)
override val root = borderpane {
center = label(currentRound.stringBinding{ "Round %d".format(it) })
// other layout-related stuff
subscribe<EndTurnEvent> {
endPlayerTurn()
}
}
private fun endPlayerTurn() {
// increment turn
currentTurn.plus(1)
currentRound.plus(currentTurn.value % 2)
}
}
that is subscribed to EndTurnEvent - event emitted by one of the fragments used by view.
The called method is supposed to increment value of currentTurn and if needed currentRound (round increments after second player ends their turn)
However neither the value of currentRound nor the one of currentTurn are getting increased when i call .plus() method.
I have tried editting values differently :
private fun endPlayerTurn() {
// increment turn
currentTurn.value = currentTurn.value + 1
currentRound.value = currentTurn.value % 2
}
But this throws me java.lang.IllegalStateException: Not on FX application thread
I am aware that putting properties into views is anti-pattern, but since I just want to keep track of 2 properties, I thought I could put them directly into View
Platform.runLater(() -> {
// Update GUI from another Thread here
});

How to remove old ArrayList items while simultaneously accessing new ones

I have a program that reads in a text file, adds certain objects to an ArrayList, then accesses them later in order to display the objects. However, the file is very big (8.5 million lines) and thus so is the ArrayList (which is what I assume causes my program to hang as soon as I run it.)
I would like to remove the older items in the ArrayList while I am accessing newer ones, so that I can keep the ArrayList small in size. It doesn't seem to be working with my current code- is this the right way to remove older items in an ArrayList?
Here is the relevant code:
void drawPoints() {
for (int i = 0; i < places.size(); i++) {
places.get(i).placeCoordinate();
places.get(i).fadeCoordinate();
if (i >= 1) {
places.remove(i - 1);
}
}
}
Some notes:
places is the ArrayList that at this point should be filled with objects from the file.
The function drawPoints() is called in the main draw() loop.
If you have a size you want to enforce, let's say 10, then you can simply do a check whenever you add something to the ArrayList:
ArrayList<Thing> list = new ArrayList<Thing>();
list.add(thing);
if(list.size() == 10){
list.remove(0); //removes the oldest thing
}
Or if you really want to remove stuff while you're looping over the ArrayList, you could simply loop backwards so the shifting of indexes doesn't interfere with the loop variable:
for (int i = places.size()-1; i >= 0; i--) {
places.get(i).placeCoordinate();
places.get(i).fadeCoordinate();
if (i >= 1) {
places.remove(i - 1);
}
}
Or you could use an Iterator:
ArrayList<Thing> list = new ArrayList<Thing>();
//create your Iterator
Iterator<Thing> iterator = list.iterator();
//loop over every Thing in the ArrayList
while(iterator.hasNext()){
Thing thing = iterator.next();
thing.doSomething();
if(thing.shouldBeRemoved()){
//Iterator allows you to remove stuff without messing up your loop
iterator.remove();
}
}