【问题标题】:What is the most efficient way to access particular elements in a SortedSet?访问 SortedSet 中特定元素的最有效方法是什么?
【发布时间】:2011-07-17 01:58:30
【问题描述】:

我想使用一个已排序的集合,但我可以通过索引访问其中的元素,即我想要同时具有 Set 和 List 特征的东西。 Java.util.TreeSet 非常接近我的需要,但不允许通过索引访问。

我能想到几个选项:

  1. 每次需要特定元素时,我都可以遍历 TreeSet。
  2. 当我需要访问特定元素时,我可以维护一个 TreeSet 并从中生成一个列表。
  3. 同上,只缓存 List 直到 Set 改变。
  4. 我可以有一个 List 并在需要添加元素时自行对其进行排序。

在各种选项之间存在各种权衡。我希望有人能给我一些好的建议。要回答有关“您为什么要这样做?”的潜在问题,请阅读Apriori 算法。

【问题讨论】:

标签: java list performance set


【解决方案1】:

https://github.com/geniot/indexed-tree-map

我遇到了同样的问题。于是我拿了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);
}

该实现基于在红黑树更改时更新节点权重。权重是给定节点下的子节点数加一 - self。例如当一棵树向左旋转时:

    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;
}

你可以在https://github.com/geniot/indexed-tree-map找到这项工作的结果

【讨论】:

    【解决方案2】:

    几点:

    • 有点无法回答,但是当我最后一次需要重新实现频繁项集挖掘算法时,我选择了 FP-growth,它的性能与先验相当(或更好),并且,在我看来,更容易实现。该技术是由 Jiawei Han 等人开发的,基本上在 Data Mining: Concepts and Techniques 中有专门的章节。

    • 有几个开源工具可以接受非常标准化的输入(每行一个整数列表;整数表示项,行表示项集)。其中一些为您提供算法选择。其中许多都可以在此处获得许可许可证:http://fimi.ua.ac.be/src/

    • 请记住,仅使用任何 List 实现不会让您访问 O(1) 元素,除非您专门使用数组/向量。更有可能的是,保留一个大部分或完全排序的数组(使用二进制搜索查找超过特定限制的元素,以及用于随机访问的常规索引),您将获得更好的里程数。

    【讨论】:

    • 感谢算法和实现指针。我查看了其他一些现有的开源实现,但我查看的那些往往有一些与我需要的不太匹配的假设(数据类型等)。 Apriori 似乎相对简单,并且对于我的任务来说可能具有足够好的效率。我对FP-growth不太熟悉。我会看看。谢谢! (为你 +1)
    • @kc2001:不客气。 “更容易实施”可能是相当主观的;他们可能处于同等水平。不过,FP-growth 更有趣一点,而且绝对更接近最先进的水平。
    【解决方案3】:

    也许 Treeset 和 apache commons collections API CollectionUtils.get() 的结合可以解决您的问题

    【讨论】:

      【解决方案4】:

      我会调查LinkedHashSet。它维护 HashSet 的插入顺序。

      【讨论】:

        猜你喜欢
        • 2014-09-24
        • 2019-06-30
        • 1970-01-01
        • 2016-01-16
        • 1970-01-01
        • 1970-01-01
        • 2016-12-17
        • 2014-08-10
        • 1970-01-01
        相关资源
        最近更新 更多