@@ -19,9 +19,42 @@ namespace osu.Game.Rulesets.UI
1919{
2020 public partial class HitObjectContainer : PooledDrawableWithLifetimeContainer < HitObjectLifetimeEntry , DrawableHitObject > , IHitObjectContainer
2121 {
22- public IEnumerable < DrawableHitObject > Objects => InternalChildren . Cast < DrawableHitObject > ( ) . OrderBy ( h => h . HitObject . StartTime ) ;
22+ /// <summary>
23+ /// All <see cref="DrawableHitObject"/>s in this container, sorted by ascending <see cref="HitObject.StartTime"/>.
24+ /// </summary>
25+ /// <remarks>
26+ /// Since internal children are already sorted by descending <see cref="HitObject.StartTime"/>
27+ /// (via <see cref="Compare"/>), we reverse-enumerate to avoid an O(n log n) sort on every access.
28+ /// </remarks>
29+ public IEnumerable < DrawableHitObject > Objects => enumerateByStartTimeAscending ( ) ;
30+
31+ /// <summary>
32+ /// All alive <see cref="DrawableHitObject"/>s in this container, sorted by ascending <see cref="HitObject.StartTime"/>.
33+ /// </summary>
34+ /// <remarks>
35+ /// The alive entries dictionary is unordered, so we must sort.
36+ /// However, the alive set is typically much smaller than the full set, making this cheaper
37+ /// than sorting all children. We use a List + Sort (in-place) to avoid LINQ iterator allocations.
38+ /// </remarks>
39+ public IEnumerable < DrawableHitObject > AliveObjects => getSortedAliveObjects ( ) ;
40+
41+ private IEnumerable < DrawableHitObject > enumerateByStartTimeAscending ( )
42+ {
43+ var children = InternalChildren ;
44+
45+ for ( int i = children . Count - 1 ; i >= 0 ; i -- )
46+ {
47+ if ( children [ i ] is DrawableHitObject hitObject )
48+ yield return hitObject ;
49+ }
50+ }
2351
24- public IEnumerable < DrawableHitObject > AliveObjects => AliveEntries . Values . OrderBy ( h => h . HitObject . StartTime ) ;
52+ private List < DrawableHitObject > getSortedAliveObjects ( )
53+ {
54+ var list = new List < DrawableHitObject > ( AliveEntries . Values ) ;
55+ list . Sort ( static ( a , b ) => a . HitObject . StartTime . CompareTo ( b . HitObject . StartTime ) ) ;
56+ return list ;
57+ }
2558
2659 /// <summary>
2760 /// Invoked when a <see cref="DrawableHitObject"/> is judged.
0 commit comments