首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何以编程方式按Kotlin中对象的名称搜索对象(数组)?

如何以编程方式按Kotlin中对象的名称搜索对象(数组)?
EN

Stack Overflow用户
提问于 2022-06-15 21:26:28
回答 2查看 172关注 0票数 0

我需要为我刚开始制作的应用程序做一些复杂的事情,我不知道我会怎么做。一切都是这样运作的。我有一个包含“房间”的对象,玩家已经生成和探索了。这些“房间”中的每一个都是另一个对象,包含该特定房间的数据,如其位置,或该房间中的项目。第一个对象中也有一个数组,它包含所有的"room“对象。当用户生成以前没有访问过的新房间时,将以编程方式创建这些对象,并且对象的名称将根据房间的位置生成(我也不知道该如何做,但这是未来的问题)。

我需要做的第一件事是搜索第一个名为“Room”的对象中的数组,以寻找另一个名称与播放器位置匹配的对象。这是由3个变量决定的,这些变量决定了玩家在房间网格上的位置。所以0,0,0将是起始位置,例如,如果玩家向右移动1个房间,向后移动2个房间,然后向上移动1层,玩家的位置将是X: 1,Y:-2,Z: 1。这个房间的对象名为"R1_n2_1“,所以游戏将搜索一个名为"R1_n2_1”的对象,并在找到它时加载它。但是,如果没有具有匹配名称的对象,那么它将生成一个新房间,并将这个新房间的数据添加为另一个对象,该对象的名称与它正在寻找的对象(“R1_n2_1”)相匹配,然后新对象也将被添加到指定所有这些“房间”/objects的数组中(正如前面提到的那样,稍后我将知道这一点)。

我需要的是如何通过编程方式生成要查找的对象名,然后查找其名称与之匹配的对象名称,从而搜索对象名称。例如,我的当前代码几乎肯定无法工作,但它会传递想法,生成它正在寻找的对象名称的字符串,然后搜索数组以找到匹配的对象。但是,它将永远找不到这个匹配,因为它正在寻找一个字符串,而不是一个具有匹配名称的对象。

代码语言:javascript
复制
class GameActivity : AppCompatActivity() {

private var exitedFrom = "start" //start; l; r; t; b; tunnel; ladder; ?;
private var Xcord: Long = 0
private var Ycord: Long = 0
private var Zcord: Long = 0

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_game)

    fullscreen()
    generateRoom()
}

private fun generateRoom() {

    val room = "R$Xcord"+"_$Ycord"+"_$Zcord"
    if (Rooms.rooms.contains(room)) {
        //load the room
        println("room $room found")
    } else println("room $room not found.") //result is always this  

}

这是我所有房间的对象。模板将被克隆以生成一个新的房间,而R0__0是开始的空间。

代码语言:javascript
复制
object Rooms {

    object Template {
    var x = 0
    var y = 0
    var z = 0
    var exitT = false
    var exitB = false
    var exitL = false
    var exitR = false
    }
    
    object R0_0_0 { //R(x)_(y)_(z)
    var x = 0
    var y = 0
    var z = 0
    var exitT = true
    var exitB = true
    var exitL = true
    var exitR = true
    }

    var rooms = arrayListOf(Template, R0_0_0)
    
}

更新:我的第一个想法就是根本不关心对象名,搜索数组中列出的每个对象,以匹配位置变量(x、y、z)。但是,这是行不通的,因为一旦数组有了多个对象,它就不能搜索数组中的所有内容,以便在任何变量中匹配。这是一种功能:

代码语言:javascript
复制
    private fun generateRoom() {

    Rooms.rooms.forEach {
        if(it.x == Xcord && it.y == Ycord && it.z == Zcord) {
            println("Room Found")
        }
    }
}

有办法绕过这件事还是我该做点别的?“数据类”能代替对象吗?我不知道数据类是如何工作的,但它们听起来很有希望。我已经开始尝试一些东西,但我不知道它将如何工作。任何其他的想法或工作解决方案将是非常有帮助的!

最后一个问题:您能用SharedPreferences或在线数据库保存这些对象吗?如果是,如何保存?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-06-16 01:51:22

我想首先指出几点:

  • 您应该尽可能避免使用包含可变状态的object (var属性或引用可变类实例的val属性)。这往往会使代码变得脆弱(很容易引入bug),因为共享状态可以很容易地从任何地方更改,并且使编写测试代码变得非常困难。如果你对它们不小心的话,它们也会导致内存泄漏。
  • ,即使您将TemplateR0_0_0转换为类,它们也是一种荒谬的设计。它们具有相同的属性,因此它们应该是同一个类。在类名中有坐标是没有意义的。如果您有一个10x10x10网格,您是否要用相同的属性手动定义1000个类?您只需要在运行时生成一个不确定的实例数的类。

  • ,您不应该将游戏状态存储在活动中。活动是短暂的。如果用户旋转屏幕或在键盘上接电话或插头等,活动实例将被销毁,您将丢失游戏数据。解决这一难题的办法是将游戏类放入Parcelable中,并使用onSavedInstanceState对其进行备份,并在配置更改后恢复它。简单的解决方案是将那些表示游戏状态的属性移动到ViewModel.
  • Don't中--如果可以的话--使用字符串来表示状态。这是一段脆弱的代码,您可以很容易地在其中输入错误,而IDE将无法先发制人地指出错误。枚举类更适合表示exitedFrom变量.

至于你要问的问题,这是一个适合MutableMap的自然选择。一个映射通过唯一的键保存条目,在映射中的每个键都可以保存一个值(在本例中,是Room类的一个实例)。映射被设计成能够非常有效地按键查找值,但是如果需要这样做,您也可以高效地迭代它们以找到一些东西。

键应该是正确实现hashcode()equals()的类的成员,这很容易使用数据类自动完成。在这种情况下,自然键类型将是一个表示坐标的类。选择一个不可变的键类也很重要,以避免潜在的bug,因此这个坐标类的所有属性都应该是val的。

因此,把所有这些放在一起,下面是我如何修复您的代码,并提供了几个示例函数来回答您关于查找房间的问题。

代码语言:javascript
复制
// Classes representing game state:

// This class is immutable and a data class so we can safely use it as a Map key.
// When we want to make changes, we use the copy() function to create
// a new instance of the class.
data class Coordinate(
    val x: Long = 0,
    val y: Long = 0,
    val z: Long = 0
) {

    // Convenience functions:
    fun movedByX(amount: Long) = copy(x = x + amount)
    fun movedByY(amount: Long) = copy(y = y + amount)
    fun movedByZ(amount: Long) = copy(z = z + amount)
}

class Room(val coordinate: Coordinate) {
    var exitT = true
    var exitB = true
    var exitL = true
    var exitR = true

    // I assume you'll add other properties such as whether it has a ladder
    // going up and/or down, etc.
)


// Enum to replace the String you were using for tracking movement direction
enum class Movement {
    Start, Left, Right, Top, Bottom, UpLadder, DownLadder
}
代码语言:javascript
复制
class GameActivity : AppCompatActivity() {

    // Read the Android docs about ViewModels to understand why we
    // instantiate it this way.
    val viewModel: GameViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_game)

        fullscreen()

        // Set up the UI that draws stuff based on properties of the 
        // viewModel and calls functions of the viewModel when the user
        // clicks stuff.
    }
}
代码语言:javascript
复制
class GameViewModel: ViewModel() {
    var playerCoordinate = Coordinate()
    private var exitedFrom = Movement.Start

    // The Map containing all rooms that have been created in the game session.
    private val rooms = mutableMapOf<Coordinate, Room>()

    // This optional property is just a convenient shortcut.
    val currentRoom: Room
        get() = rooms.getValue(playerCoordinate)

    init {
        // Create the initial room by moving there
        move(Movement.Start)
    }

    fun move(movement: Movement) {
        exitedFrom = movement // I'm not sure if you really need this exitedFrom
                              // property but if you do, you can back it up here.
        playerCoordinate = when (movement) {
            Start -> Coordinate()
            Left -> playerCoordinate.moveX(-1L)
            Right -> playerCoordinate.moveX(1L)
            Bottom -> playerCoordinate.moveY(-1L)
            Top -> playerCoordinate.moveY(1L)
            DownLadder -> playerCoordinate.moveZ(-1L)
            UpLadder -> playerCoordinate.moveZ(1L)
        }

        // Create the room if it doesn't exist yet.
        if (!rooms.containsKey(playerCoordinate)) {
            val newRoom = Room(playerCoordinate).apply {
                // Customize the new room's properties here. Maybe randomized?
                // probably want to use the movement to ensure there is an exit
                // coming from the direction that was moved from so player
                // can backtrack.
            }
            rooms[playerCoordinate] = newRoom 
        }
    }

    fun resetGame() {
        rooms.clear()
        move(Movement.Start)
    }
}
票数 1
EN

Stack Overflow用户

发布于 2022-06-16 01:53:55

可以通过对象属性、对象类型或对象类型的字符串表示形式搜索对象列表。最后一个是(我认为)您想要做的事情(但它可能不是最可扩展的代码设计选择)。

按对象属性搜索

如果您只想根据对象的属性搜索对象列表,可以使用列表上的find函数搜索与某些谓词匹配的对象,如下所示:

代码语言:javascript
复制
class Room(var name: String)

fun hasRoom(name: String, rooms: List<Room>): Boolean {
    return rooms.find { it.name == name } != null
}

val rooms = listOf(Room("A"), Room("B"), Room("C"))

hasRoom("A", rooms) // true
hasRoom("D", rooms) // false

您还可以为此操作或其他常见操作为您的房间列表定义扩展函数:

代码语言:javascript
复制
fun List<Room>.containsName(name: String): Boolean {
    return this.find { it.name == name } != null
}

这将使您可以直接在房间列表上调用containsName,类似于您正在尝试做的事情:

代码语言:javascript
复制
val rooms = listOf(Room("A"), Room("B"), Room("C"))

rooms.containsName("A") // true
rooms.containsName("D") // false

另外,正如另一个答案所指出的那样,您肯定应该在这里使用一个带有属性的类,而不是为每个坐标使用一个新的类类型。坐标可以是类的属性,上面的属性搜索方法仍然适用。

按对象类型搜索

如果您真的想按对象类型而不是对象的属性搜索对象列表,仍然可以使用扩展函数进行搜索:

代码语言:javascript
复制
class Room
class House
class Yacht
class Boat

inline fun <reified K> List<Any>.containsObj(): Boolean {
    return this.filterIsInstance<K>().isNotEmpty()
}

val rooms = listOf(Room(), House(), Yacht())

rooms.containsObj<House>() // true
rooms.containsObj<Boat>() // false

按对象类型名称搜索

如果您真的想根据类型的字符串名称进行搜索,您也可以这样做。

代码语言:javascript
复制
fun List<Any>.containsObjName(name: String): Boolean {
    return this.find { it::class.simpleName == name } != null
}

val rooms = listOf(Room(), House(), Yacht())

rooms.containsObjName("House") // true
rooms.containsObjName("Boat") // false
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72638011

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档