java - Java 类 TreeSet 中的 Java 方法 headSet 和 tailSet 是否在 log(N) 时间内工作?
问题描述
在 JavaDoc 中写到,TreeSet 上的基本操作在 log(N) 时间内工作,其中 N 是集合的大小。在我看来,如果集合足够大,headSet 和 tailSet 方法应该通过二进制搜索之类的方法找到它们正在计算的视图的开头,但 JavaDoc 对此保持沉默。
解决方案
文档没有提到headSet
andtailSet
的时间复杂度。他们只说:
返回此集合中元素严格小于 toElement 的部分的视图。返回的集合由该集合支持,因此返回集合中的更改会反映在该集合中,反之亦然。返回的集合支持该集合支持的所有可选集合操作。
(我强调视图)。而视图确实是最重要的部分。创建视图始终是一个O(1)
操作,最坏的情况是,只创建包装类。没有执行关键字搜索,只是类型检查,实际上也没有触发其他操作。
这是TreeSet.headSet(E toElement)
代码:
public SortedSet<E> headSet(E toElement) {
return headSet(toElement, false);
}
这是TreeSet.headSet(E toElement, boolean inclusive)
代码:
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
return new TreeSet<>(m.headMap(toElement, inclusive));
}
如您所知,TreeSet
由TreeMap
实例支持。这就是m
财产的实际情况。因此,上面的操作委托给该TreeMap.headMap(E toElement, boolean inclusive)
方法,然后创建一个由 .返回的TreeSet
实例支持的新实例。NavigableMap
TreeMap.headMap(E toElement, boolean inclusive)
首先,让我们看看TreeSet
构造函数:
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
如您所见,这只是一个任务。
然后,让我们深入研究该TreeMap.headMap(E toElement, boolean inclusive)
方法的代码:
public NavigableMap<K,V> headMap(K toKey, boolean inclusive) {
return new AscendingSubMap<>(this,
true, null, true,
false, toKey, inclusive);
}
这只是创建并返回AscendingSubMap
静态嵌套类的实例。这是AscendingSubMap
构造函数的代码:
AscendingSubMap(TreeMap<K,V> m,
boolean fromStart, K lo, boolean loInclusive,
boolean toEnd, K hi, boolean hiInclusive) {
super(m, fromStart, lo, loInclusive, toEnd, hi, hiInclusive);
}
这只是委托给超类的构造函数(AscendingSubMap
的超类是NavigableSubMap
静态嵌套抽象类)。这是NavigableSubMap
构造函数的代码:
NavigableSubMap(TreeMap<K,V> m,
boolean fromStart, K lo, boolean loInclusive,
boolean toEnd, K hi, boolean hiInclusive) {
if (!fromStart && !toEnd) {
if (m.compare(lo, hi) > 0)
throw new IllegalArgumentException("fromKey > toKey");
} else {
if (!fromStart) // type check
m.compare(lo, lo);
if (!toEnd)
m.compare(hi, hi);
}
this.m = m;
this.fromStart = fromStart;
this.lo = lo;
this.loInclusive = loInclusive;
this.toEnd = toEnd;
this.hi = hi;
this.hiInclusive = hiInclusive;
}
如您所见,这只是检查参数的正确性并将它们分配给属性。
这m
是封闭TreeMap
实例(请记住,这NavigableSubMap
是一个静态嵌套抽象类)。TreeMap.compare(Object k1, Object k2)
方法如下:
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
这个方法只是根据需要比较它的参数,在这里它只是用来检查子图的边界。(请记住,TreeMap
键可以是也可以Comparable
不是。如果不是,则在构造实例Comparator
时必须提供a ,这就是上面代码中的属性)。TreeMap
comparator
这就是调用该headSet
方法时所做的一切。tailSet
遵循相同的模式(只是最终子图的边界不同)。
因此,作为结论,headSet
实际上tailSet
是O(1)
最坏的情况。
注意:我已经检查了JDK 8 v1.8.0_181
和openjdk version "11" 2018-09-25
版本的代码。我很确定中间版本也没有改变。
编辑:
关于访问由. headSet
_ O(logN)
_实现为红/黑树)。TreeSet
TreeSet
TreeMap
迭代返回的集合视图tailSet
具有相同的时间复杂度:O(logN)
. 这是因为实现需要对值更接近提供的下限的节点进行搜索,并且该搜索也是O(logN)
.
推荐阅读
- javascript - 如何使用 maptalks 仅显示精确区域(精确区域的图块)?
- mysql - 只有一排返回!但所有用户的预期输出
- android - 存储在 Room 表中的设置在任何参数更新时触发所有观察者
- java - org.springframework.beans.factory.BeanCreationException 用于 HibernateTemplate 类依赖注入
- jenkins - Jenkins Github-plugin 无法创建 webhook
- mysql - TimeStamp CURRENT_TIMESTAMP 显示不正确的时间(提前 2 小时)
- xcode - 如何修复 install_name_tool:无法在 xcode 10.2 中打开文件
- android - 如何更改图像视图的半径模糊
- c# - 如何在 TreeView,wpf 中将组合框添加为 TreeViewItem?
- ionic4 - Ionic4 输入类型号接受验证