You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java/Kotlin中高效查找时间重叠事件的最优实现方案

高效区间查询的Java/Kotlin实现方案(低GC占用)

核心思路

要满足O(logn)查询复杂度且低GC开销,区间树是正确方向,但需针对GC问题做针对性优化——核心是避免频繁创建临时对象,复用已有集合/结构;静态数据场景下,排序+二分查找结合后缀最大值优化也是高效且低GC的选择。

优化版区间树实现(支持动态数据)

定义区间对象

Kotlin

data class Interval(val id: String, val start: Long, val end: Long)

Java

public class Interval {
    private final String id;
    private final long start;
    private final long end;

    public Interval(String id, long start, long end) {
        this.id = id;
        this.start = start;
        this.end = end;
    }

    public String getId() { return id; }
    public long getStart() { return start; }
    public long getEnd() { return end; }
}

区间树实现(低GC优化)

通过复用结果集合、预计算节点最大end值来减少临时对象创建:

Kotlin实现

class IntervalTree(private val intervals: List<Interval>) {
    private val root: Node?

    init {
        root = buildTree(intervals.sortedBy { it.start })
    }

    private class Node(val interval: Interval, val maxEnd: Long, val left: Node?, val right: Node?)

    private fun buildTree(sortedIntervals: List<Interval>): Node? {
        if (sortedIntervals.isEmpty()) return null
        val mid = sortedIntervals.size / 2
        val midInterval = sortedIntervals[mid]
        val left = buildTree(sortedIntervals.subList(0, mid))
        val right = buildTree(sortedIntervals.subList(mid + 1, sortedIntervals.size))
        val maxEnd = maxOf(
            midInterval.end,
            left?.maxEnd ?: Long.MIN_VALUE,
            right?.maxEnd ?: Long.MIN_VALUE
        )
        return Node(midInterval, maxEnd, left, right)
    }

    // 复用传入的结果列表,避免频繁创建新集合
    fun query(target: Long, result: MutableList<Interval>) {
        result.clear()
        query(root, target, result)
    }

    private fun query(node: Node?, target: Long, result: MutableList<Interval>) {
        if (node == null) return
        // 当前区间包含目标值,加入结果
        if (node.interval.start <= target && target <= node.interval.end) {
            result.add(node.interval)
        }
        // 左子树可能存在符合条件的区间
        if (node.left != null && node.left.maxEnd >= target) {
            query(node.left, target, result)
        }
        // 右子树可能存在符合条件的区间
        if (node.right != null && node.right.interval.start <= target) {
            query(node.right, target, result)
        }
    }

    // 迭代版实现,避免递归栈溢出,进一步优化性能
    fun queryIterative(target: Long, result: MutableList<Interval>) {
        result.clear()
        val stack = ArrayDeque<Node>()
        root?.let { stack.push(it) }
        while (stack.isNotEmpty()) {
            val node = stack.pop()
            if (node.interval.start <= target && target <= node.interval.end) {
                result.add(node.interval)
            }
            // 先压右节点,保证左节点优先处理
            if (node.right != null && node.right.interval.start <= target) {
                stack.push(node.right)
            }
            if (node.left != null && node.left.maxEnd >= target) {
                stack.push(node.left)
            }
        }
    }
}

关键GC优化点

  • 复用结果集合:要求调用方传入可变列表,查询前清空再添加,避免每次创建新List对象。
  • 预计算maxEnd:节点存储子树最大end值,避免查询时重复计算,减少临时变量。
  • 迭代替代递归:避免递归带来的栈帧开销,同时降低潜在的内存波动。

静态数据场景:排序+二分查找方案

如果区间不会动态增删,该方案实现简单、GC开销极低,查询效率接近O(logn):

Kotlin实现

class StaticIntervalQuery(sortedIntervals: List<Interval>) {
    private val sortedIntervals: List<Interval>
    private val suffixMaxEnd: LongArray

    init {
        this.sortedIntervals = sortedIntervals.sortedBy { it.start }
        this.suffixMaxEnd = LongArray(this.sortedIntervals.size).apply {
            if (isEmpty()) return@apply
            this[this.sortedIntervals.size - 1] = this@sortedIntervals.last().end
            for (i in this.sortedIntervals.size - 2 downTo 0) {
                this[i] = maxOf(this@sortedIntervals[i].end, this[i + 1])
            }
        }
    }

    fun query(target: Long, result: MutableList<Interval>) {
        result.clear()
        // 二分找到最大的start<=target的区间索引
        var left = 0
        var right = sortedIntervals.size - 1
        var maxIdx = -1
        while (left <= right) {
            val mid = (left + right) / 2
            if (sortedIntervals[mid].start <= target) {
                maxIdx = mid
                left = mid + 1
            } else {
                right = mid - 1
            }
        }
        if (maxIdx == -1) return
        // 遍历筛选,利用后缀maxEnd提前终止
        for (i in 0..maxIdx) {
            if (suffixMaxEnd[i] < target) break
            if (sortedIntervals[i].end >= target) {
                result.add(sortedIntervals[i])
            }
        }
    }
}

方案选择建议

  • 动态数据(需增删区间):使用优化版区间树,迭代实现优先,保证O(logn)查询复杂度与低GC开销。
  • 静态数据:选择排序+二分查找方案,实现简单、性能稳定、GC压力极小。

内容的提问来源于stack exchange,提问作者Uriel Frankel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 12:54:53