【问题标题】:Adding or moving an element in a list already sorted在已排序的列表中添加或移动元素
【发布时间】:2022-01-07 09:08:32
【问题描述】:

我正在寻找一种在已排序列表中添加元素或移动现有元素的方法。

data class House(
    val id: Int,
    val sold: Boolean,
    val creationDate: LocalDate,
    ...
)

我的列表已经按 2 个参数(sold 和 creationDate)排序。

Example of a list already sorted :
[
    House(123, false, 2015-03-23, ...),
    House(456, false, 2015-01-11, ...),
    House(789, false, 2014-08-30, ...), // <- I'm looking for move this one
    House(147, false, 2014-02-15, ...),
    House(258, true, 2016-06-02, ...),
    House(369, true, 2015-04-20, ...),
    House(321, true, 2015-01-28, ...),
    House(654, true, 2011-11-01, ...)
]

现在,如果房屋已售出,(ID 为 789 的房屋)如何在不重新排序所有列表的情况下将项目从列表的第一部分移动到另一部分。 (出于性能考虑) 这个新职位必须考虑关键的“creationDate”。

List updated :
[
    House(123, false, 2015-03-23, ...),
    House(456, false, 2015-01-11, ...),
    House(147, false, 2014-02-15, ...),
    House(258, true, 2016-06-02, ...),
    House(369, true, 2015-04-20, ...),
    House(321, true, 2015-01-28, ...),
    House(789, true, 2014-08-30, ...), // <- Result expected
    House(654, true, 2011-11-01, ...)
]

感谢您的帮助。

【问题讨论】:

  • 你的目标是 JVM 吗?这听起来像是 PriorityQueue 而不是列表的用例。
  • 我认为仅对整个列表进行排序就不会成为性能问题。但是,是的,知道一种更有效的方法会很有趣

标签: list sorting kotlin


【解决方案1】:

假设你有:

val list = mutableListOf<House>(
    ...
)
val indexToMove = 2

您可以先删除元素,然后使用二分搜索查找应插入的位置。如果找不到元素,binarySearch 将返回 (-insertion point - 1)。如果列表中恰好有相同的元素,我们将使用该元素的索引作为插入点。

val removed = list.removeAt(indexToMove)
removed.sold = true // shouldn't "sold" be a var if a house can be sold?

// this should be the same comparator you used for sorting the list
val comparator: Comparator<House> = 
    compareBy(House::sold)
    .thenByDescending(House::creationDate)
val searchResult = list.binarySearch(removed, comparator)
val insertionPoint =
    if (searchResult < 0) {
        -(searchResult + 1)
    } else {
        searchResult
    }
list.add(insertionPoint, removed)

【讨论】:

  • 非常感谢。工作就像一个魅力,它除以 4 到 5 的工作时间与我以前对整个列表重新排序的方式相比。 (有时接近 10)
【解决方案2】:

基于您的列表已经排序的假设,您可以从列表中删除房屋,然后将其重新插入到正确的位置。已售房屋必须插入到您要插入的房屋之后的第一所房屋的索引处。

在这种情况下,您正在寻找已售出且创建日期早于您刚售出的房屋的第一套房子:

val index = houseList.indexOfFirst { 
    it.sold && it.creationDate < soldHouse.creationDate 
}

从列表中删除房屋并将其转换为已售房屋,这给出了

val houseList = mutableListOf(
    House(123, false, LocalDate.of(2015, 3, 23)),
    House(456, false, LocalDate.of(2015, 1, 11)),
    House(789, false, LocalDate.of(2014, 8, 30)),
    House(147, false, LocalDate.of(2014, 2, 15)),
    House(258, true, LocalDate.of(2016, 6, 2)),
    House(369, true, LocalDate.of(2015, 4, 20)),
    House(321, true, LocalDate.of(2015, 1, 28)),
    House(654, true, LocalDate.of(2011, 11, 1))
)

println("old house list:")
println(houseList.joinToString(", ") {it.id.toString()})

val houseToSell = houseList.firstOrNull { it.id == 789 } ?: return
houseList.remove(houseToSell)
val soldHouse = houseToSell.copy(sold = true)

val index = houseList.indexOfFirst { it.sold && it.creationDate < soldHouse.creationDate }
if (index == -1) houseList.add(0, soldHouse)
else houseList.add(index, soldHouse)

println("new house list:")
println(houseList.joinToString(", ") {it.id.toString()})
    

打印出来的

old house list:
123, 456, 789, 147, 258, 369, 321, 654
new house list:
123, 456, 147, 258, 369, 321, 789, 654

【讨论】:

  • 这是个好方法。比重新排序列表更好。但要小心你的提议indexOfFirst 可以返回-1。
  • @Ben 对,很好。我已经更新了答案。
猜你喜欢
  • 1970-01-01
  • 2018-08-31
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
  • 1970-01-01
  • 2011-10-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多