我想使用一个已排序的集合,但我可以通过索引访问元素,即我想要一些具有Set和List特征的东西。 Java.util.TreeSet非常接近我的需求,但不允许通过索引进行访问。
我可以想到几个选项:
各种选择之间存在各种权衡。我希望有人能给我一些好的建议。要回答关于“你为什么要这样做?”的潜在问题,请阅读Apriori算法。
答案 0 :(得分:2)
有几点:
非答案的排序,但是当我最后需要重新实现频繁项集挖掘算法时,我选择了FP-growth,其性能与先验相当(或更好),并且在我看来,更容易实现。这个技术是由Jiawei Han和其他人开发的,基本上有一个专门的章节 Data Mining:Concepts and Techniques 。
有几个开源工具采用非常标准化的输入(每行一个整数列表;整数表示项目,行表示项目集)。其中一些为您提供了算法选择。其中许多都可以使用许可许可证:http://fimi.ua.ac.be/src/
请注意,除非您专门使用数组/向量,否则仅使用任何List
实现都不会获得O(1)
元素访问权限。更有可能的是,你可以获得更好的里程数来保持一个大多数或完全排序的数组(使用二进制搜索来查找特定限制的元素,以及通常的随机访问索引)。
答案 1 :(得分:1)
我遇到了同样的问题。所以我获取了java.util.TreeMap的源代码并编写了 IndexedTreeMap 。它实现了我自己的 IndexedNavigableMap :
public interface IndexedNavigableMap<K, V> extends NavigableMap<K, V> {
K exactKey(int index);
Entry<K, V> exactEntry(int index);
int keyIndex(K k);
}
实现基于更改红黑树中的节点权重。权重是给定节点下的子节点数加一个自身。例如,当树向左旋转时:
private void rotateLeft(Entry<K, V> p) {
if (p != null) {
Entry<K, V> r = p.right;
int delta = getWeight(r.left) - getWeight(p.right);
p.right = r.left;
p.updateWeight(delta);
if (r.left != null) {
r.left.parent = p;
}
r.parent = p.parent;
if (p.parent == null) {
root = r;
} else if (p.parent.left == p) {
delta = getWeight(r) - getWeight(p.parent.left);
p.parent.left = r;
p.parent.updateWeight(delta);
} else {
delta = getWeight(r) - getWeight(p.parent.right);
p.parent.right = r;
p.parent.updateWeight(delta);
}
delta = getWeight(p) - getWeight(r.left);
r.left = p;
r.updateWeight(delta);
p.parent = r;
}
}
updateWeight只是更新权重到根:
void updateWeight(int delta) {
weight += delta;
Entry<K, V> p = parent;
while (p != null) {
p.weight += delta;
p = p.parent;
}
}
当我们需要通过索引找到元素时,这是使用权重的实现:
public K exactKey(int index) {
if (index < 0 || index > size() - 1) {
throw new ArrayIndexOutOfBoundsException();
}
return getExactKey(root, index);
}
private K getExactKey(Entry<K, V> e, int index) {
if (e.left == null && index == 0) {
return e.key;
}
if (e.left == null && e.right == null) {
return e.key;
}
if (e.left != null && e.left.weight > index) {
return getExactKey(e.left, index);
}
if (e.left != null && e.left.weight == index) {
return e.key;
}
return getExactKey(e.right, index - (e.left == null ? 0 : e.left.weight) - 1);
}
还可以非常方便地找到密钥的索引:
public int keyIndex(K key) {
if (key == null) {
throw new NullPointerException();
}
Entry<K, V> e = getEntry(key);
if (e == null) {
throw new NullPointerException();
}
if (e == root) {
return getWeight(e) - getWeight(e.right) - 1;//index to return
}
int index = 0;
int cmp;
index += getWeight(e.left);
Entry<K, V> p = e.parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
while (p != null) {
cmp = cpr.compare(key, p.key);
if (cmp > 0) {
index += getWeight(p.left) + 1;
}
p = p.parent;
}
} else {
Comparable<? super K> k = (Comparable<? super K>) key;
while (p != null) {
if (k.compareTo(p.key) > 0) {
index += getWeight(p.left) + 1;
}
p = p.parent;
}
}
return index;
}
找到这项工作的结果
答案 2 :(得分:0)
也许Treeset和apache commons集合API CollectionUtils.get()的组合可以解决您的问题
答案 3 :(得分:0)
我会调查LinkedHashSet。它维护HashSet的插入顺序。