【问题标题】:loop to get closest object to input value循环以获取最接近输入值的对象
【发布时间】:2013-09-19 15:54:57
【问题描述】:

我有这个火星课程:

public abstract class Martian implements Cloneable {
int id;

public Martian(int id) {
    this.id = id;
}
public Object clone() throws CloneNotSupportedException {
    return super.clone();
}
public int getId() {
    return id;
}
public boolean equals(Object o){
    if( o != null);
    return this.getId() == ((Martian)o).getId();    
}
public abstract void speak();

public String toString(){
    return "Martian" + getId(); 
}       
} 

还有一个 MartianManager 类:

public class MartianManager {
private ArrayList<Martian> martians;
private ArrayList<Martian> teleporters;

public void addMartian(Martian m) {
    martians.add(m);
    if(m instanceof GreenMartian)
        teleporters.add(m);
}
//public Object clone() {

public Martian getMartianClosestToID(int id) {

}
public void groupSpeak() {
    for(Martian m : martians) {
        m.speak();
    }
}
public void groupTeleport(String dest) {
    for (Martian m : martians){
        if (m instanceof GreenMartian)
            ((GreenMartian) m).teleport(dest);
    }   
}
//public obliterateTeleporters() 

//removeMartian(int id)
}

MartianManager 类中,我有一个方法getMartianClosestToId(),它返回与输入ID 最接近的ID 火星人。我的问题基本上是在循环中使用的最简单的逻辑是什么,或者他们是一种更简单的方法,例如compareTo,我不知道比较是否可以在这种情况下工作。

【问题讨论】:

  • 虽然很想帮你,但我强烈反对克隆火星人。
  • 不要在 Java 中使用 clone() 方法。它已经过时了。它有很多缺陷。
  • @gparyani 它并没有过时。只是不建议使用它。
  • @RohitJain 看看 Java 1.2 时代的 this bug report。在里面,他们特别说它已经过时了。
  • 你如何计算距离?有坐标吗?欧几里得距离还是曼哈顿距离?

标签: java loops arraylist compare


【解决方案1】:

这不是最简单的,但在很多情况下它会是最快的。

如果您愿意让您的火星人列表始终按 id 排序(如果您不经常添加火星人,这很容易做到,您可以在添加时进行排序),您可以这样做:

Comparator<Martian> compareById = new Comparator<Martian>() {
    public int compare(Martian a, Martian b) {
        return Integer.compare(a.getId(), b.getId());
    }
}

然后你可以在你的列表上使用二分搜索来找到它在列表中插入的位置。

int location = Collections.binarySearch(martians, idToGetClosestTo, compareById);

现在,您将拥有它应该去的地方,并且您将拥有以下五个选项之一:

  1. 该位置具有您要查找的 ID。如果是,则返回 martians.get(location);
  2. 提供的位置不在列表中,这意味着您正在寻找低于最低或高于最高位置的位置。 2.a.低于最低:返回最低,martians.get(0); 2.b。高于最高:返回最高,martians.get(martians.length()-1);
  3. 位置高于您要查找的 ID。 (它不会更低,否则你得到的结果会比你所做的低 1!)所以看看 martians.get(location) 和 martian.get(location - 1) 看看你最接近哪个并返回相应的。

这具有昂贵的前期成本(排序),但是在您对其进行排序之后,您可以使用非常便宜的二分搜索,每次都非常快速地找到最近的火星人。

如果您要经常添加,那么我建议您将新的火星人添加到末尾并将您的收藏标记为未排序,然后仅在您即将找到一个时进行排序。

public Martian getMartianClosestToID(int id) {
    if(!martiansAreSorted) Collections.sort(martians,compareById);
    int loc = Collections.binarySearch(martians,id,compareById);
    if(loc >= 0) return martians.get(loc); // found exact match
    // we know loc is negative because it wasn't found - read the docs
    loc = -loc;
    if(loc == 0) return martians.get(0);
    if(loc == martians.size()) return martians.get(loc - 1);
    Martian high = martians.get(loc);
    Martian low = martians.get(loc - 1);

    int highid = high.getId();
    int lowid = low.getId();

    int highdiff = Math.abs(id - highid);
    int lowdiff = Math.abs(id - lowid);

    if(highdiff < lowdiff) return high;
    return low;

}

【讨论】:

    【解决方案2】:

    这样的事情应该可以工作。

    这是否正是您想要的取决于一些假设。 ID 是唯一的吗?火星人能离自己最近吗?如果有两个同样接近的火星人怎么办?还是没有其他火星人?我假设 'id' 参数可能是集合中的 ID 之一 - 而您不想要那个。

    但最大的问题是:“最近”是什么意思? “亲密”的概念通常不适用于 ID。

    public Martian getMartianClosestToId(int id) {
        Martian closest = null;
        int leastDist = -1;
        for(Martian m : martians) {
            int mId = m.getId();
            if(mId == id)
                 continue; // Skip the Martian with the same id.
            int d = Math.abs(mId - id);
            if(leastDist == -1 || d < leastDist) {
                leastDist = d;
                closest = m;
            }
        }
        return closest;
    }
    

    我还没有编译/测试过这个 - 你可能需要修正错别字/错误。

    【讨论】:

    • 确实有一些错别字 - 刚刚改正了。
    • 请注意,如果列表中没有火星人,或者只有一个具有相同 ID 的火星人传递给方法,这将返回 null。
    • 请注意,如果某些 ID 非常小/大,这里的所有这些解决方案(我认为)都可能会出现问题。 (例如:两个火星人 - 一个的 ID 为 Integer.MIN_VALUE,另一个的 ID 为 Integer.MAX_VALUE。)
    【解决方案3】:

    我认为这比迄今为止的其他答案更简单(OP 要求“最简单的逻辑”):

    public Martian getMartianClosestToID(int id)
    {
        if (martians == null || martians.isEmpty())
            return null;
    
        Martian result = martians.get(0);
    
        for (Martian m : martians)
            if (Math.abs(id - m.getId()) < Math.abs(id - result.getId()))
                result = m;
    
        return result
    }
    

    【讨论】:

    • 如果列表中没有任何火星人,这将引发异常。
    猜你喜欢
    • 1970-01-01
    • 2020-11-18
    • 2018-05-03
    • 1970-01-01
    • 2020-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多