为何Swift中ClosedRange<Int>.contains性能比预期慢340万倍?
Swift中ClosedRange.contains(_: Sequence)性能异常的原因与解决方法
问题现象
使用Xcode 15.0.1(Swift 5)时,调用ClosedRange.contains(_ other: ClosedRange)的性能远低于手动边界比较逻辑,测试代码如下:
import Foundation func measure(fn: @escaping ()->Void) -> CFAbsoluteTime { let startTime = CFAbsoluteTimeGetCurrent() fn() let endTime = CFAbsoluteTimeGetCurrent() return endTime - startTime } let r1: ClosedRange<Int> = 1222450723...1308640992 let r2: ClosedRange<Int> = 41218238...462709950 let dt_a = measure { let _ = r1.lowerBound <= r2.lowerBound && r1.upperBound >= r2.upperBound } let dt_b = measure { let _ = r1.contains(r2) }
测试结果:
- 手动边界比较(dt_a)耗时约95微秒
- 调用
contains(_:)(dt_b)耗时约326.9秒(Release模式下甚至达到356秒)
两者性能差距超过340万倍。
原因解析
这是API语义误解导致的误用,并非Swift的Bug。
你调用的r1.contains(r2)并非专门用于判断“范围包含”的方法,而是Collection协议提供的contains<S>(_ other: S) -> Bool重载(其中S是序列,且元素类型与当前集合一致)。这个方法的语义是:检查当前集合是否包含另一个序列的所有元素。
由于ClosedRange遵循Collection协议,r2会被当作一个包含4亿多个元素的序列来遍历,逐个验证每个元素是否存在于r1中——这种实现完全不符合你预期的“范围边界比较”逻辑,因此导致了极端缓慢的执行速度。
解决方案
要判断一个ClosedRange是否完全包含另一个ClosedRange,直接使用边界比较逻辑即可,这是最高效的方式:
// 直接在代码中使用判断逻辑 let isContained = r1.lowerBound <= r2.lowerBound && r1.upperBound >= r2.upperBound
如果需要复用这个逻辑,可以给ClosedRange扩展一个专门的方法:
extension ClosedRange where Bound: Comparable { /// 判断当前范围是否完全包含另一个范围 func fullyContains(_ other: ClosedRange<Bound>) -> Bool { return lowerBound <= other.lowerBound && upperBound >= other.upperBound } } // 使用方式 let isContained = r1.fullyContains(r2)
注意不要直接重载contains方法,避免和原有的API语义冲突,导致代码可读性下降。
内容的提问来源于stack exchange,提问作者endavid
相关产品推荐
相关产品推荐

