【问题标题】:Insert Objects in a Constant Length List - Java在恒定长度列表中插入对象 - Java
【发布时间】:2015-12-06 19:08:51
【问题描述】:

我正在寻找一个很好的最佳策略来为以下问题编写代码。

我有一个对象列表。

  1. 对象在其他字段中有一个字符串“评估”字段。估值字段可能是唯一的,也可能不是唯一的。
  2. 列表的长度是在程序中计算的。长度通常在 100 到 500 之间。
  3. 对象都根据字符串字段在列表中排序 - 估值
  4. 发现或创建新对象时:将字符串字段估值与列表的现有成员进行比较。
  5. 如果比较失败,例如使用列表的底部成员,则该对象不会添加到列表中。
  6. 如果比较成功并且新对象被添加到列表中 - 在排序标准内;新对象被添加到正确的位置并且底部成员被从列表中移除以保持列表的长度不变。

我正在考虑的一个策略:

  1. 继续向列表中添加成员 - 直到达到 maxLength
  2. 排序 - (例如,带有比较器的 Collections.sort)列表
  3. 创建新成员时 - 将其与列表底部的成员进行比较。
  4. 如果成功 - 替换底部成员,否则继续
  5. 重新排序列表 - 如果成功

然后继续。

程序循环百万次以上,优化比较和运行成为问题。

任何关于在 Java 域中解决此问题的好策略的指南。哪些列表将是最有效的,例如LinkedList 或 ArrayLists 或 Sets 等。哪种排序/插入(标准包)最有效?

【问题讨论】:

  • 估值是否唯一?我会说使用 TreeMap 或 TreeSet 作为实现的基础。这样搜索速度很快,插入也很好。
  • @Jan.. 不,估值可能是唯一的,也可能不是唯一的。即如果等于底部成员,则 - 不要添加。我也更新了问题。
  • Still TreeSet 可能是最佳选择 - 但您需要快速比较器。
  • 感谢 @ Jan。在这里阅读 Treeset link 的一些很好的例子。这个链接也有比较和比较器的一个很好的例子。 “快速比较器”是什么意思?
  • 啊,从头到尾。无论如何,您都会比较字符串。因此,就性能而言,没有什么大不了的。

标签: java arraylist


【解决方案1】:

考虑这个基于 TreeSet 并比较结果的字符串的示例。如您所见,经过足够多的迭代后,List 中只剩下具有非常大键的元素。在我相当旧的笔记本电脑上,我在不到 50 毫秒的时间内处理了 10.000 个项目 - 所以每百万个列表操作大约需要 5 秒。

public class Valuation {

    public static class Element implements Comparable<Element> {
        String valuation;
        String data;
        Element(String v, String d) {
            valuation = v;
            data = d;
        }
        @Override
        public int compareTo(Element e) {           
            return valuation.compareTo(e.valuation);
        }       
    }

    private TreeSet<Element> ts = new TreeSet<Element>();

    private final static int LISTLENGTH = 500;

    public static void main(String[] args) {
        NumberFormat nf = new DecimalFormat("00000");
        Random r = new Random();
        Valuation v = new Valuation();
        for(long l = 1; l < 150; ++l) {
            long start = System.currentTimeMillis();
            for(int j = 0; j < 10000; ++j) {
                v.pushNew(new Element(nf.format(r.nextInt(50000))
                                , UUID.randomUUID().toString()));
            }
            System.out.println("10.000 finished in " + (System.currentTimeMillis()-start) + "ms. Set contains: " + v.ts.size());
        }
        for(Element e : v.ts) {
            System.out.println("-> " + e.valuation);
        }
    }

    private void pushNew(Element hexString) {
        if(ts.size() < LISTLENGTH) {
            ts.add(hexString);
        } else {
            if(ts.first().compareTo(hexString) < 0) {
                ts.add(hexString);
                if(ts.size() > LISTLENGTH) {
                    ts.remove(ts.first());
                }
            }
        }
    }
}

【讨论】:

  • 您不应该从集合中删除 last(最大)元素,而不是第一个吗?
  • 另外,无需检查if (ts.size() &gt; LISTLENGTH),因为您已经知道了。
【解决方案2】:

任何关于在 Java 域中解决此问题的好策略的指南。

我的建议是 - 无需进行任何排序。在向集合中添加更多对象时,您可以通过执行二进制插入来确保对数据进行排序。

这样,当您添加更多项目时,集合本身已经处于排序状态。

在第 500 项之后,如果要添加另一个,我们只需执行另一个二进制插入。插入性能始终保持在 O(log(n)),无需执行任何排序。

与您的算法比较

您的算法从 1 到 4 运行良好。但第 5 步可能会成为您算法的瓶颈:

5.重新排序列表 - 如果成功

这是因为即使您的列表最多只有 500 个项目,但在添加第 500 个项目后,可以在此列表上执行无限次插入。

想象一下再插入 100 万次,并且(在更坏的情况下),所有 100 万个项目都“成功”并可以插入到列表中,这意味着您的算法将需要执行 100 万次以上的排序! 这将是 1 million * n(log(n)) 用于排序。

与二进制插入比较,在最坏的情况下,插入将是1 million * log(n)(无排序)。

哪些列表将是最有效的,例如LinkedList 或 ArrayLists 或 Sets 等。

如果您使用 ArrayList,则插入不会像链表那样有效,因为 ArrayList 是由数组支持的。然而,与 O(n) 的链表相比,arrayList 的元素访问仅为 O(1)。所以没有一种对所有场景都有效的数据结构。您必须先规划您的算法,然后看看哪一个最适合您的策略。

哪种排序/插入(标准包)最有效?

据我所知,有 Arrays.sort()Collections.sort() 可用,因为它们使用双轴排序,这将比自己创建的简单插入/冒泡/选择排序。

【讨论】:

  • 另一种选择是使用普通的旧数组,因为大小是恒定的。
  • @KlitosKyriacou 这可能是其中一种选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多