【问题标题】:Efficient way of comparing two arraylists in order to retain a delta between them比较两个数组列表以保留它们之间的增量的有效方法
【发布时间】:2015-11-12 00:06:20
【问题描述】:

这是我遇到的问题:

我需要比较两个 ArrayList 并返回它们是否相同或不同,返回其中一个的新元素,可以说是枢轴。

这是数据集的行为:

  • 这两个 ArrayList 是由字符串组成的
  • 它们是从同一来源填充的,因此大部分时间都是相同的
  • 是有序的(就附加到它们的自定义逻辑而言)
  • 永远不会有空字符串
  • 所有字符串的长度始终相同
  • 没有重复

我想要什么:

  • 实现我的两个目标的最快方法,无论是哪种情况
  • 仅使用 Java 1.6 标准库功能,我不希望实现一个混合类,例如模拟 List 和 Set 中的某些内容。

例子:

A: [ 'a', 'b', 'c', 'd']   

B: [ 'a', 'c', 'd']

结果:列表不同,返回元素'b'; A 将是“工作”列表,我们将根据这个 ArrayList 中的新内容进行比较,因为 B 永远不会改变。

感谢您的任何回复和您的意见。

【问题讨论】:

  • ordered 是指这些列表按字母顺序排序,或者每个List<String> 中的元素遵循某种顺序,所以我们可以说list1.get(0).equals(list2.get(0))?跨度>
  • 你已经尝试了什么?
  • 这会有帮助吗? stackoverflow.com/a/30107086/441902
  • 同一个字符串可以多次包含在一个列表中吗? (或者可以将它们转换为Sets 而不会丢失信息?)。 @ 否决/接近投票者:给这个问题一个机会。这不是很好,但还有更糟糕的......
  • @Luigi:有序意味着它们在填充两个数组列表时,插入顺序与它们的“排序”顺序相同。它永远不会改变。每当一个新元素被引入其中一个 arrayLists 时,它总是比他旁边的下一个更小/更大。

标签: java algorithm arraylist data-structures set


【解决方案1】:

你的最快要求让我很困扰——我非常反对优化——我通常认为早期优化是最糟糕的编程实践之一。

如果您真的想这样做,只需按顺序遍历这两个列表即可。

如果第一个条目匹配,则将其放入“相同”堆并增加两个索引。如果它们不同,则将第一个(小于/小于)一个放入“不同”堆并增加列出索引的值。以这种方式循环,直到您到达一个列表的末尾(另一个列表中剩余的任何显然都会进入“不同”集合。

这应该让您“接近”最快的方式。如果你想要绝对最快的,那么你必须从使用数组开始,而不是列表,然后非常注意你在每一步中所做的其他事情——但算法应该仍然非常接近最优。

作为次优但更具可读性的示例,您可以使用一些集合操作。

Set set1=new HashSet(list1)
Set set2=new HashSet(list2)
Set same=set1.retainAll(set2) // I forget if retainAll modifies set1--if so you need to copy it first
set1.removeAll(list2)
set2.removeAll(list1)
Set different=set1.addAll(set2)

// at this point same contains all the similar values and different contains the ones that don't match.  Done.

这是简短易读的,并且可能比您想象的更高效。如果这样的代码运行良好(例如,在不太可能影响速度的 GUI 代码中),编写自己的代码将是一种不好的做法。

【讨论】:

  • 我非常反对优化 使用最佳算法(例如您在此处提供的算法)与使用已知的蛮力慢速算法产生一个过程是完全不同的或者(甚至更糟)过早的优化。尽管如此,由于这个答案提供了一种简单而好的方法来实现它,假设这些列表始终是排序的,并且当您遍历它们以比较结果时,数据不会改变。
  • 我想我同意@LuiggiMendoza。不过,我通常表达它的方式是我从不预先优化,但我认为很多代码是“错误的”,比如对 ArrayList 的插入排序或对 LinkedList 的索引访问。除了那些糟糕的代码之外,我总是更愿意看到一个可读的解决方案而不是最好的。
  • 主要逻辑比这复杂一点。但是我已经实现了与 HashSets 的比较。这整个问题基本上是我只是在问“对于我遇到的这个问题,我是否错过了一种更快的方法来解决它?”......从所有回复中,我认为不是。
  • @user3046061 看起来您正在为您当前的解决方案寻找一个微型基准测试并提出一个新的解决方案。尽管如此,由于 BillK 解释的原因,我还是建议使用 Set 方法。如果你注意到这个算法是你的应用程序中使用分析器证明的瓶颈,那么你必须开始寻找改进,否则,你有一个黄金法则:如果没有损坏,就不要修复它
  • @user3046061 是的,我认为并行遍历列表的速度与您获得的速度差不多,并且如果您不计算创建初始值的时间,它可能仍与哈希集解决方案相当2 个哈希或对原始的两个列表进行排序。创建哈希/排序列表可能是其中最耗时的部分。
【解决方案2】:

很简单(假设列表是升序排列,可以很容易地更改为降序排列):

ArrayList<String> delta(ArrayList<String> a , ArrayList<String> b , Comparator<String> comp){
    if(a.isEmpty())
        return new ArrayList(b);
    if(b.isEmpty())
        return new ArrayList(a);

    Iterator<String> it_a = a.iterator();
    Iterator<String> it_b = b.iterator();

    ArrayList<String> delta = new ArrayList<>();

    String a_s = it_a.next() , b_s = it_b.next();
    boolean onechecked = false;

    while(!onechecked){
        int comp_v = comp.compare(a_s , b_s);

        if(comp_v == 0){
            //strings are equal -> ommit them
            if(it_a.hasNext())
                a_s = it_a.next();
            else
                onechecked = true;

            if(it_b.hasNext())
                b_s = it_b.next();
            else
                onechecked = true;
        }else if(comp_v < 0){
            //a_s is not part of b
            delta.add(a_s);
            if(it_a.hasNext())
                a_s = it_a.next();
            else
                onechecked = true;
        }else{
            //b_s is not part of a
            delta.add(b_s);
            if(it_b.hasNext())
                b_s = it_b.next();
            else
                onechecked = true;
        }
    }

    //add remaining items
    delta.add(it_a.hasNext() ? a_s : b_s);

    for(Iterator<String> it = (it_a.hasNext() ? it_a : it_b) ; it.hasNext();)
        delta.add(it.next());

    return delta;
}

很抱歉没有添加任何解释,但代码必须自己说话,因为我不知道如何解释它。

【讨论】:

  • 感谢代码回复。我会根据我已经知道的情况来检查它的性能。
  • 这个答案对我很有帮助。我正在比较两个列表,每个列表大约有 40,000 个条目,并寻找增量。海报没有解释代码,但本质上,您首先同时遍历两个列表并比较每个列表中的第一项:如果它们匹配,则移动到两个列表中的下一项;如果它们不匹配,则将缺少的项目添加到增量列表中,并仅推进缺少项目的列表。到达任一列表的末尾后,添加未完成列表中的剩余项目。这将 5 分钟的过程变成了不到 10 秒。
猜你喜欢
  • 2014-05-15
  • 2011-04-10
  • 2012-06-04
  • 2021-04-29
  • 1970-01-01
  • 2016-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多