2014-10-04 4 views
0

행운없이 모델의 모든 점 목록을 만들려고 노력했습니다. 내가 이것을 실행할 때HashMap 중복시에도 모든 항목 추가

HashList<Point> points=new HashList<Point>(16); 

    //add +y side 
    points.add(new Point(-5.0,5.0,-5.0)); 
    points.add(new Point(-5.0,5.0,5.0)); 
    points.add(new Point(5.0,5.0,5.0)); 

    points.add(new Point(-5.0,5.0,-5.0)); 
    points.add(new Point(5.0,5.0,5.0)); 
    points.add(new Point(5.0,5.0,-5.0)); 

    //add -x side 
    points.add(new Point(-5.0,5.0,-5.0)); 
    points.add(new Point(-5.0,-5.0,-5.0)); 
    points.add(new Point(-5.0,-5.0,5.0)); 

    points.add(new Point(-5.0,5.0,-5.0)); 
    points.add(new Point(-5.0,-5.0,5.0)); 
    points.add(new Point(-5.0,5.0,5.0)); 

    int length=points.length(); //equals 12, 6 expected 

    Point a=new Point(-5.0,5.0,-5.0); 
    Point b=new Point(-5.0,5.0,-5.0); 
    int aHashCode=a.hashCode(); //-737148544 
    int bHashCode=b.hashCode(); //-737148544 
    boolean equals=a.equals(b); //true 

포인트는 내가 시작한 번호 인 12 포인트를 포함한다. 내가 발견 한 모든 중복은 테이블에서 단지 6 점이되어야한다.

if (map.containsKey(e)) { 

(HashList에서) 어떤 이유로 든 실행되지 않습니다. 어떤 아이디어?

HashList :

package dataTypes; 

import java.util.ArrayList; 
import java.util.HashMap; 

public class HashList<E> { 
    private HashMap<E,Integer> map; 
    private ArrayList<E> data; 
    private int count=0; 

    public HashList() { 
     map=new HashMap<E,Integer>(); 
     data=new ArrayList<E>(); 
    } 

    public HashList(int size) { 
     map=new HashMap<E,Integer>(size); 
     data=new ArrayList<E>(size); 
    } 

    public int add(E e) { //returns key 
     if (map.containsKey(e)) { 
      return map.get(e); 
     } else { 
      map.put(e, count); 
      data.add(count,e); 
      return count++; 
     } 
    } 

    public int getKey(E e) { 
     return map.get(e); 
    } 

    public E get(int key) { 
     return data.get(key); 
    } 

    public int length() { 
     return count; 
    } 
} 

점 :

rolfl가 눈치
package geometry3D; 

/** 
* 3D point location or vector 
* 
* @author Matthew Cornelisse 
* @version 2014-09-02-004500 
*/ 
public class Point 
{ 
    // instance variables - replace the example below with your own 
    public double x; 
    public double y; 
    public double z; 

    /** 
    * Constructor for objects of class Point 
    */ 
    public Point() 
    { 
     // initialise instance variables 
     x = 0; 
     y = 0; 
     z = 0; 
    } 

    public Point(double x, double y, double z) 
    { 
     this.x=x; 
     this.y=y; 
     this.z=z; 
    } 
    public Point(Point a) { 
     x=a.x; 
     y=a.y; 
     z=a.z; 
    } 

    /** 
    * Normailizes the point to have distance from center of 1 
    * 
    */ 
    public void normalize() 
    { 
     // put your code here 
     double length=Math.sqrt(x*x+y*y+z*z); 
     x/=length; 
     y/=length; 
     z/=length; 
    } 



    //implements Shape 
    public void rotateX(double angle){ 
     double newY=Math.cos(angle)*y-Math.sin(angle)*z; 
     double newZ=Math.sin(angle)*y+Math.cos(angle)*z; 
     y=newY; 
     z=newZ; 
    } 
    public void rotateY(double angle){ 
     double newX=Math.cos(angle)*x-Math.sin(angle)*z; 
     double newZ=Math.sin(angle)*x+Math.cos(angle)*z; 
     x=newX; 
     z=newZ; 
    } 
    public void rotateZ(double angle){ 
     double newX=Math.cos(angle)*x-Math.sin(angle)*y; 
     double newY=Math.sin(angle)*x+Math.cos(angle)*y; 
     x=newX; 
     y=newY; 
    } 
    public void rotate(Vector axis, double angle){ 
     //source: http://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/ 
     double oldX=x; 
     double oldY=y; 
     double oldZ=z; 
     double sinA=Math.sin(angle); 
     double cosA=Math.cos(angle); 
     Point offset=axis.offset(); 
     Point vector=axis.vector(); 
     double u=vector.x; 
     double v=vector.y; 
     double w=vector.z; 
     double a=offset.x; 
     double b=offset.y; 
     double c=offset.z; 


     x=(a*(v*v+w*w)-u*(b*v+c*w-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldX*cosA+(-c*v+b*w-w*oldY+v*oldZ)*sinA; 
     y=(b*(u*u+w*w)-v*(a*u+c*w-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldY*cosA+(c*u-a*w+w*oldX-u*oldZ)*sinA; 
     z=(c*(u*u+v*v)-w*(a*u+b*v-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldZ*cosA+(-b*u+a*v-v*oldX+u*oldY)*sinA; 



    } 
    public void move(double x, double y, double z){ 
     this.x+=x; 
     this.y+=y; 
     this.z+=z; 
    } 
    public void move(Vector direction,double magnitude){ 
     this.x+=(direction.vector().x*magnitude); 
     this.y+=(direction.vector().y*magnitude); 
     this.z+=(direction.vector().z*magnitude); 
    } 

    public boolean equals(Point compare) { 
     if (Math.abs(compare.x-x)>5*Math.ulp(compare.x)) return false; 
     if (Math.abs(compare.y-y)>5*Math.ulp(compare.y)) return false; 
     if (Math.abs(compare.z-z)>5*Math.ulp(compare.z)) return false; 
     return true; 
    } 
    public boolean equals(Point compare, double error) { 
     if (Math.abs(compare.x-x)>error) return false; 
     if (Math.abs(compare.y-y)>error) return false; 
     if (Math.abs(compare.z-z)>error) return false; 
     return true; 
    } 

    public int hashCode(){ 
     Double a=(Double)x; 
     Double b=(Double)y; 
     Double c=(Double)z; 
     return a.hashCode()^Integer.rotateRight(b.hashCode(),12)^Integer.rotateRight(c.hashCode(),24); 
    } 

    public boolean equals(Object compare) { 
     try { 
      Point temp=(Point)compare; 
      if (temp.x!=x) return false; 
      if (temp.y!=y) return false; 
      if (temp.z!=z) return false; 
      return true; 
     } finally { 
      return false; 
     }  
    } 
} 
+0

왜 'Set'을 사용하지 않겠습니까? –

+1

다른 것들과는 달리,'equals' 메쏘드에서와 같이'try/finally'를 사용하는 것은 * horrible *입니다. 대신에'instanceof'를 사용하십시오. –

+0

다음으로 문제를 보여주는 짧지만 완전한 * 프로그램을 제공해주십시오. 우리는 같은 점 등을 만들기 위해 코드를 추가 할 필요가 없습니다. (점의 거의 모든 코드는 아무런 회전도하지 않으므로 질문과 관련이 없습니다) –

답변

0

finally 블록의 문제는 equals(Object)입니다. 블록에서 true을 반환하는 경우에도 은 항상이며, false을 반환합니다.

boolean equals=a.equals(b); //true 

을 ...하지만 그 같은 equals 메소드를 호출 아니에요 - 그것은 equals(Point)를 부르고 :

당신은 이것 때문에 혼란스러워지고 있습니다.

귀하의 equals(Object) 방법은 다음과 같이 기록한다 :

@Override public boolean equals(Object compare) { 
    if (compare == this) { 
     return true; 
    } 
    if (compare == null || compare.getClass() != getClass()) { 
     return false; 
    } 
    Point temp = (Point) compare; // Guaranteed to work now 
    return temp.x == x && temp.y == y && temp.z == z; 
} 

하는 것으로 : 다른 곳에서 언급 한 바와 같이

  • , 당신은 허용 오차를 처리하는 평등/해시 ... 경우에 올 수 없습니다 당신은이 점들과 함께 중요한 산술 연산을 수행합니다. 더 이상 정확한 평등을 이루지 못할 것입니다.
  • 클래스에 공개 입력란이 있습니다. 꽤 많이 있습니다. 항상이 좋지 않습니다. 아이디어
  • 클래스는 해시 코드를 사용하여 코드의 관점에서 나쁜 생각이다, 변경 가능한 - 컬렉션은을 많은 요소의 형태는
  • 불변 때 클래스를 만들 경우 올바르게 사용하기 쉽게 finalgetClass() 체크 대신에 instanceof을 사용할 수 있습니다. 다시 말해, 누군가가 변경 가능한 서브 클래스를 도입하는 것을 방지하는 데 도움이됩니다. (상속 계층에 걸친 평등 관계는 일반적으로 고통 스럽다.)
+0

저는 제 2의 다른 equals 함수를 almostEquals로 이름을 바꾸고 이것을 구현했으며 완벽하게 작동했습니다. 고맙습니다. –

1

그것이 자바를 사용하려고 소용이없는 허용 오차 포인트를 비교하는 방법을 동일 (참조 TL/DR 의심스러운 경우 아래 섹션 참조).

이렇게 어려운 일을해야합니다. map.containsKey(e)을 사용한다고 상상하지 말고 HashList에 명시적인 방법을 만듭니다.

public interface DistEquality<E> { 
    boolean distEquals(E compare); 
} 

그런 다음 그것을 구현하는 Point를 선언한다 : 그 인터페이스에 의해 시작될 것 HashList에게 난 아무것도 테스트하지 않았습니다

public class HashList<E extends DistEquality<E>> { 
    ... 
    public int distValue(E e) { 
     for (Entry<E, Integer> entry: map.entrySet()) { 
      if (e.distEquals(entry.getKey())) { 
       return entry.getValue(); 
      } 
     } 
     return -1; 
    } 

    public int add(E e) { //returns key 
     int pos = distValue(e); 
     if (pos != -1) { 
      return pos; 
     } else { 
      map.put(e, count); 
      data.add(count,e); 
      return count++; 
     } 
    } 
    ... 
} 

그런 식으로

public class Point implements DistEquality<Point> { 
    ... 
    public static double defaultError = 10E-3; 
    @Override 
    public boolean distEquals(Point compare) { 
     return equals(compare, defaultError); 
    } 
} 

을 수정,하지만 난 생각 일반적인 생각은 좋아야합니다.

TL/솔루션 아래 DR 은 명백히 잘못이다 - (몰래에 rolfl 감사는) 클래스 Point에서

equals 방법은 두 배의 정확한 평등을 필요로한다. 대신 적절한 값으로 초기화 된 static double defaultErrorPoint의 클래스를 가지고 다음 수행해야합니다

public boolean equals(Object compare) { 
    return ((compare instanceof Point) ? equals(compare, defaultError) : false); 
} 

그러나

rolfl에 의해 발견으로 Object.hashCode() 상태에 대한 javadoc는이 이 만약 객체가에 따라 동일하기 때문에,이 충분하지 않습니다 equals (Object) 메소드를 호출 한 다음 두 객체 각각에 대해 hashCode 메소드를 호출하면 동일한 정수 결과을 생성해야합니다.

위의 등식 방법과 호환되는 지능형 해시를 상상하는 것은 매우 어려울 것입니다. 더 나쁜 것은, 한 포인트가 hashCode 값을 얻으면 다른 포인트는 defaultError의 유한 수이고, 2 등분 당 2 인 완성 된 포인트 스위트를 상상할 수 있으므로 hashCode는 상수 여야합니다.

심하게는 반사음, 대칭 및 이행성이 같아야하므로 모든 점은 동일해야합니다.

그것은 동일한 그런 식으로 사용하는 아이디어는 정말

+0

이것은 부분 대답 일뿐입니다. hashCode를 수정하지 않고 equals를 수정해도 도움이되지 않습니다. – rolfl

+0

@rolfl : 맞습니다. 그러나 equals 메소드는 JDK 계약을 준수하지 않습니다. 내 편집 참조 –

+0

자바를 처음 사용하지만이 수정으로 인해 다른 객체에서 작동하지 않습니다 유형? 클래스는 generic이며 모든 객체 유형에서 사용할 수 있어야합니다. 또한이 특정 사례에서 내가 찾고있는 요점은 똑같은 지점 세트 (예를 들어 큐브는 8 점이지만 삼각형으로 모델링하려면 36 점이 필요합니다.)에서 모두 존재해야하는 것과 정확히 같습니다. 한 번 입력하면 소프트웨어에 포인트를 정확히 똑같이 조작해야합니다. 그래서 모두 정확히 같은 값을 유지해야합니다. –

0

당신이 중복을 포함 할 수 있도록하려는 경우 HashList 구현이 잘못 :-(나쁜 생각이라고 본다.추가 할 때지도에 이미 키가있는 경우 값을 반환하면 복제본이 삽입되지 않습니다.

중복되는 경우 해당 포인트에 대한 Equals/HashCode가 엉망이 될 가능성이 있습니다.

지금 당장은 HashList가 실제로 중복을 허용하지 않으므로이를 제거하고 Java 컬렉션의 HashSet을 사용할 수 있습니다. 또한, netbeans, 이클립스, IntellIJ와 같은 괜찮은 자바 IDE를 얻고 당신의 포인트 클래스에서 당신을 위해 equals/hashcode를 생성해야합니다.

+0

중복되는 것을 원하지 않습니다. 문제는 내가 중복을 얻고있다. –

+0

또한 이클립스를 사용하고있다. –

+0

중복을 원하지 않는다면 왜 HashSet을 사용하지 않는가? 어쨌든 HashCode/Equals 구현에 문제가있을 가능성이 큽니다. 해당 항목을 HashSet에 추가하고 dups가 계속 표시되는지 확인하십시오. 그렇게한다면 Equals/HashCode가 엉망이된다는 것을 알게 될 것입니다. –