Java:验证列表中是否已经存在对象?

Java:验证列表中是否已经存在对象?,java,list,Java,List,这里详细描述了这个问题: 简而言之:由于文本文件中给出的指示,我连续访问了多个地点,我想知道我至少访问过多少个地点 我创建了一个类点,如下所示: public class Point { private int x; private int y; public Point (int x, int y) { this.x=x; this.y=y; } public int getAbs() { return x; } public int

这里详细描述了这个问题: 简而言之:由于文本文件中给出的指示,我连续访问了多个地点,我想知道我至少访问过多少个地点

我创建了一个类点,如下所示:

public class Point {
  private int x;
  private int y;

  public Point (int x, int y) {
    this.x=x;
    this.y=y;
  }

  public int getAbs() {
    return x;
  }

  public int getOrd() {
    return y;
  }
}
它只是一个由两个坐标组成的点,用来表示房子的位置。 我在这里使用它:

public class Exo3 {

  public static void main(String[] args) throws IOException {
    FileReader fr = new FileReader("C:/Users/mpv15_000/Documents/Advent/input3.txt");   
    int c = fr.read();

    int x=0;
    int y=0;
    Point init = new Point(x,y);

    List<Point> visitedHouses = new ArrayList<Point>();
    visitedHouses.add(init);

    Boolean b = true;

    while ((c == '^') || (c == 'v') || (c == '<') || (c == '>'))
    {           
      if (c == '^')  
        y++;
      else if (c == 'v')
        y--;    
      else if (c == '<')
        x--;
      else
        x++;

      for (Point p : visitedHouses) {
        if ((p.getAbs() != x) || (p.getOrd() != y))
          b = true;
        else 
          b = false;
      }     

      if (b == true) {
        visitedHouses.add(new Point(x,y));
      }
      c = fr.read();
    }

    System.out.println("Number of houses visited at least once : " + visitedHouses.size());
    fr.close();
  }
}
公共类Exo3{
公共静态void main(字符串[]args)引发IOException{
FileReader fr=新的FileReader(“C:/Users/mpv15_000/Documents/Advent/input3.txt”);
int c=fr.read();
int x=0;
int y=0;
点初始=新点(x,y);
List visitedHouses=new ArrayList();
访问房屋。添加(初始);
布尔b=真;
while((c='^')| |(c=='v')| |(c=='')
{           
如果(c=='^')
y++;
else如果(c='v')
y--;

else if(c=='当
x
y
相同时,需要重写Point类中的
equals
方法以返回true。 大概是这样的:

  @Override
  public boolean equals(Object other)
  {
      if (this == other) return true;
      if (!(other instanceof Point)) return false;
      return (this.x == ((Point)other).x) && (this.y == ((Point)other).y);

  }
然后使用集合而不是列表。集合将只包含唯一的点

如果您想继续使用列表,只需执行以下操作:

    if (b == true)
    {
        Point visitedPoint = new Point(x,y);
        if (!visitedHouses.contains(visitedPoint)) {
           visitedHouses.add(visitedPoint);
        }
    }
顺便说一句:如何识别到访的房屋取决于您……

代码部分

    for (Point p : visitedHouses)
    {
        if ((p.getAbs() != x) || (p.getOrd() != y))
            b = true;
        else 
            b = false;
    }       

    if (b == true)
    {
        visitedHouses.add(new Point(x,y));
    }
看起来问题特别严重

如果你在列表中间找到“被访问”的房子,下一个条目不会(希望)是那个房子的复制品,并将“B”标志设为false,导致重复。

这是由于“缓存控制变量”的编程气味造成的。我建议您不要存储控制变量(稍后在if语句中签入的变量),以改变块的行为

Point newCandidate = new Point(x,y);
for (Point p : visitedHouses) {
    if (newCandidate.getAbs() == p.getAbs() &&
        newCandidate.getOrd() == p.getOrd()) {
      newCandidate = null;
      break;
    }
}
if (newCandidate != null) {
    visitedHouses.add(newCandidate);
}
但是,这仍然很像一个控制变量。如果我们只使用java
Set
HashSet
那样的函数,速度会快得多

public Point {
   ... same stuff you have ...

   @Override
   public int hashcode() {
       return 37*x + 23*y + 13;
   }

   @Override
   public boolean equals(Object object) {
       if (! object instanceof Point) {
           return false;
       }
       Point other = (Point)object;
       if (this.x != other.x) { return false; }
       if (this.y != other.y) { return false; }
       return true;
   }
}
那你就可以了

Set<Point> visitedHouses = new HashSet<Point>();
// possibly initialize the first house
visitedHouses.add(new Point(0, 0));
while (... visiting each house ...) {
    Point currentHouse = new Point(currentX, currentY);
    visitedHouses.add(currentHouse);
}
Set visitedHouses=new HashSet();
//可能是第一栋房子
添加(新点(0,0));
当(…参观每家…){
点currentHouse=新点(currentX,currentY);
访问房屋。添加(当前房屋);
}
并依靠
集合
界面确保在
集合
中永远不会获得重复的
条目,因为
集合
不能包含重复项(并且您的
现在知道如何获取两个实例并确定它们是否相等)


不仅最后一种方法要快得多,因为它存储在
散列集中
您不需要遍历
集中的每个
来确定您是否与当前位置匹配。
散列码(…)
在“用于查询集的点”上运行通过返回值进行查找可以避免99%的其他条目。然后将具有匹配哈希代码的一小部分条目与
equals(…)
方法进行比较,以确定它们是否等效,或者它们是否“刚刚发生”要使用相同的hashcode。

如上述问题注释中所述,您需要在找到重复点后立即中断VisitedHouse循环。
对于性能,我在循环中稍微更改了条件,请参见以下内容:

  boolean found = false;
  for (Point p : visitedHouses) {
    if ((p.getAbs() == x) && (p.getOrd() == y)) {
      found = true;
      break;
    }
  }     

  if (!found) {
    visitedHouses.add(new Point(x,y));
  }

我不确定我是否完全理解这个问题,但是如果你想跟踪你是否看到了什么,我会使用集合而不是列表。或者为了检查成员身份,将列表放入集合。
集合
不能解决这个问题吗?(除了实现
hashCode
equals
)是的,你说得对,我复制代码时出错,代码是x和y。我编辑。@MaxZoom排序列表有O(log(n))个插入和查找时间。
HashSet
有O(1)个插入和查找时间。“最佳方法”是主观的。无论哪种方式,实现
hashCode
equals
都应该是这里的关键。如果
if
语句没有必要,则内部
语句是不必要的。
Set
会自动拒绝添加元素,如果它已经包含在
集合中。使用
java.util.Objects.hash(x,y)
对于
hashCode
来说,它可能没有那么“神奇”我不确定你认为我的评论是什么意思。我只是建议你使用内置的哈希算法而不是手动算法,以避免在实现
点时使用不必要的幻数。hashCode
@4castle我可能误解了你所说的“幻数”如果是散列算法,那么你的陈述更有意义(我之前的回答大多没有抓住要点)。对于那些想“解释”我的算法的人来说,基本上是观察到相对素数在一个数字集中有很好的分布。因此,你可以取
(一些素数)*x+(一些其他素数)*y+一些第三个素数
,除非你非常不走运,否则你可能有一个相对素数分布,这是一个非零的起点,这使得散列非常合适。