【问题标题】:Efficiently comparing two lists of objects in Java有效地比较 Java 中的两个对象列表
【发布时间】:2018-04-16 11:20:29
【问题描述】:

我目前正在查看两个非常大的Peak 对象列表,方法是重写equals 方法并循环遍历这两个列表,将每个峰值与其他峰值进行比较。有没有更有效的方法来做到这一点?我的列表可以是 ~10,000 个元素,这意味着最多 10000 * 10000 次比较。

我的峰值对象的代码:

public class Peak extends Object{

private final SimpleIntegerProperty peakStart;
private final SimpleIntegerProperty peakEnd;
private final SimpleIntegerProperty peakMaxima;
private final SimpleIntegerProperty peakHeight;
private final SimpleIntegerProperty peakWidth;
private final SimpleStringProperty rname;

public Peak(int peakStart, int peakEnd, int peakMaxima, int peakHeight, String rname) {
    this.peakStart = new SimpleIntegerProperty(peakStart);
    this.peakEnd = new SimpleIntegerProperty(peakEnd);
    this.peakMaxima = new SimpleIntegerProperty(peakMaxima);
    this.peakHeight = new SimpleIntegerProperty(peakHeight);
    this.peakWidth = new SimpleIntegerProperty(peakEnd - peakStart);
    this.rname = new SimpleStringProperty(rname);
}

public String getRname() {
    return rname.get();
}

public SimpleStringProperty rnameProperty() {
    return rname;
}

public int getPeakWidth() {
    return peakWidth.get();
}

public int getPeakHeight() {
    return peakHeight.get();
}

public int getPeakStart() {
    return peakStart.get();
}

public int getPeakEnd() {
    return peakEnd.get();
}

public int getPeakMaxima() {
    return peakMaxima.get();
}

@Override
public String toString() {
    return "Peak{" +
            "peakStart= " + peakStart.get() +
            ", peakEnd= " + peakEnd.get() +
            ", peakHeight= " + peakHeight.get() +
            ", rname= " + rname.get() +
            '}';
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Peak peak = (Peak) o;

    if (!peakMaxima.equals(peak.peakMaxima)) return false;
    return rname.equals(peak.rname);
}

@Override
public int hashCode() {
    int result = peakMaxima.hashCode();
    result = 31 * result + rname.hashCode();
    return result;
}
}

我比较对象的循环在这里。

 List<Peak> interestingPeaks = new ArrayList<>();

            if(peakListOne != null && peakListTwo != null){
                for(Peak peak : peakListOne){
                    for(Peak peak2 : peakListTwo){
                        if(peak.equals(peak2)){ //number one, check the rnames match
                            if((peak2.getPeakHeight() / peak.getPeakHeight() >= 9) || (peak.getPeakHeight() / peak2.getPeakHeight() >= 9)){
                                    interestingPeaks.add(peak);
                            }
                        }
                    }
                }
            }

            return interestingPeaks;

代码基本上匹配最大值的位置,以及 rname ,它只是一个字符串。然后将峰值附加到interestingPeaks 列表中,如果一个的高度比另一个大 9 倍。

【问题讨论】:

  • 你尝试过使用哈希吗?
  • 如果您对列表进行排序,您可以更智能地进行比较。
  • 有些我们不知道的情况可能会使您的算法更简单或更困难。列表可以排序还是顺序很重要?单个列表中是否允许重复峰值(可能是 HashSet)?有许多算法可以帮助你,但你必须看看可能的方法是否符合你的具体情况。
  • 列表未按输入排序,但没有理由不能将它们作为方法的一部分进行排序。

标签: java performance


【解决方案1】:

如果这两个列表按最大值和名称排序,您可以简单地对两个列表进行一次线性传递,并并排比较项目。如果这两个列表实际上完全相等,那么您永远不会从两个列表中找到不相等的对。

List<Peak> p1;
List<Peak> p2;

p1.sort((p1, p2) -> {
    int comp = Integer.compare(p1.getPeakMaxima(), p2.getPeakMaxima());
    return comp != 0 ? comp : p1.getRname().compareTo(p2.getRname());
});

// and also sort the second list

现在我们可以遍历两个列表并检查比较失败:

for (int i=0; i < p1.size(); ++i) {
    if (!p1.get(i).equals(p2.get(i))) {
        System.out.println("peaks are not equal");
        break;
    }
}

这将O(N^2) 操作减少为O(N*lgN) 操作,这是执行这两种排序的惩罚(最后遍历列表是O(N),任何一种方法都可以忽略不计)。

【讨论】:

  • O(n^2) 减少到 O(n)。这是一个相当大的优化——当且仅当列表被允许重新排序时。 OP 从来没有提到过,所以虽然是一个很好的答案,但我们只能希望它会有用;)
  • @mingos 我回答不完整。我正在更新。
  • 我绝对可以对列表进行排序。这将完美地工作。谢谢。
  • @Sam 您是想标记每个不匹配的峰,还是只想断言所有峰都相同或不同?如果是后者,你就完成了,我的回答应该有效。对于前者,我的回答是一个好的开始,但我们需要做更多的工作。
  • @TimBiegeleisen 我只需要知道 peakMaximas 和 rnames 匹配的位置,因此您的示例肯定会起作用。再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-17
  • 2020-04-26
相关资源
最近更新 更多