Java:验证List中是否已存在对象?

时间:2017-07-11 20:11:10

标签: java list

此处详细描述了问题:https://adventofcode.com/2015/day/3 总结一下:由于文本文件中的指示,我先后访问了地点,我想知道我至少访问过多少个地点。

我创建了一个类Point:

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();
  }
}

我得到“8193”,这是我想的迭代总数,但不是我想要的。 我想知道我至少访问过一次的位置。 我认为问题来自于visitedHouses列表中的for循环。 我想比较当前的x和y是否已存在于此列表中的Point中。我该怎么办?

3 个答案:

答案 0 :(得分:1)

equalsx相同时,您需要覆盖Point类中的y方法以返回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);

  }

然后使用Set而不是List。 Set将仅保留唯一的Points。

如果您想继续使用List,请执行以下操作:

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

答案 1 :(得分:1)

代码部分

    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,从而导致重复。< / p>

这是由于编程气味“缓存控制变量”。我建议你不要存储控制变量(后来在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);
}

但是,这仍然主要是控制变量。如果我们只使用像Set这样的java 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界面确保您永远不会在Point中获得重复的Collection条目,因为Set不能包含重复项(以及您的{{1}现在知道如何获取两个实例并确定它们是否相等。

不仅最后一种方法会更快,因为它存储在Point中,您无需通过HashSet中的每个Point来确定您是否拥有匹配当前位置。 Set在“用于查询Set的点”上运行,并且通过它的返回值查找避免了99%的其他条目。然后将具有匹配哈希码的小项子集与hashcode(...)方法进行比较,以确定它们是否等效,或者它们是否“恰好”具有相同的哈希码。

答案 2 :(得分:0)

正如上面的问题评论中所提到的,只要找到重复的点,就需要打破 visitedHouses 循环。
为了提高性能,我在循环中稍微改变了条件,见下文:

  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));
  }

DEMO