为什么我这个类的compareTo方法没有返回边<a,b>和<b,a>同样的东西?

时间:2016-11-18 12:50:55

标签: java graph

这里(下面)是我的MovieEdge班级;没什么特别的,它指向2个Actor个对象,它们是这个边缘的顶点。此外,这是一个未加权和无向图,但我们需要添加weight字段,即使它始终初始化为1,因此可以在compareTo方法中忽略权重。

所以在我的graph课程中,我调用我的方法edgeSet,该方法返回一组所有边但是它是一个集合,所以它应该只添加唯一的边缘但是取下面的这两个边缘

Actor a = new Actor("James");
Actor b = new Actor("Dan");
MovieEdge one = new MovieEdge(a, b, 1, "Movie1");
MovieEdge one = new MovieEdge(b, a, 1, "Movie1");

比较上面的2个MovieEdges时,我的compareTo方法应输出0,因为这是一个无向图,但是当我在main方法中调用以下内容时

    testGraph.addEdge(A, C, 1, "Movie1");
    testGraph.addEdge(B, C, 1, "Movie1");
    testGraph.addEdge(C, D, 1, "Movie3");
    testGraph.addEdge(D, A, 1, "Movie1");
    testGraph.addEdge(A, D, 1, "Movie1");
    testGraph.addEdge(A, E, 1, "Movie1");
    System.out.println(testGraph.edgeSet());

在我的main方法中,这是输出

[A via Movie1 to C, A via Movie1 to D, A via Movie1 to E, B via Movie1 to C, C via Movie1 to A, E via Movie1 to A, C via Movie3 to D]

注意集合如何显示 A via Movie1 to EE via Movie1 to A 那是不对的

我从凌晨2点开始改变我的'compareTo'(现在是早上7:43),但我不明白,我的前2个条件返回0应该解决这个问题,但由于某种原因它不是。

另外我应该指出,奇怪的是当我只添加一个像testGraph.addEdge(A, E, 1, "Movie1");这样的边缘并调用System.out.println(testGraph.edgeSet());时,它会正确打印并且只在控制台上打印[A via Movie1 to E]

public class MovieEdge implements Comparable {

    private Actor source;
    private Actor destination;
    private int weight;
    private String movieName;

    public MovieEdge(Actor source, Actor destination, int wieght, String movieName){
        this.source = source;
        this.destination = destination;
        this.weight = weight;
        this.movieName = movieName;
    }

    public Actor getSource() {
        return source;
    }

    public Actor getDestination() {
        return destination;
    }

    public int getWeight(){
        return weight;
    }

    public String getMovieName() {
        return movieName;
    }

    public String toString(){
        return source + " via " + movieName + " to " + destination ;
    }

    @Override
    public int compareTo(Object o) {
        MovieEdge a = (MovieEdge)o;
        if(this.movieName.equals(a.movieName)){
            if(this.source.getName().equals(a.source.getName()) && this.destination.getName().equals(a.destination.getName())){
                return 0;
            }
            else if(this.source.getName().equals(a.destination.getName()) && this.destination.getName().equals(a.source.getName())){
                return 0;
            }
            else if(this.destination.compareTo(a.destination) != 0 || this.source.compareTo(a.source) !=0 ){
                return 1;
            }
            return 0;
        }
        if(this.movieName.compareTo(a.movieName) < 0){
            return -1;
        }else if(this.movieName.compareTo(a.movieName) > 0){
            return 1;
        }


        return -1;
    }

}

我的edgeSet方法

public Set<MovieEdge> edgeSet() {
    Set<MovieEdge> values = new TreeSet<MovieEdge>();

    for (TreeSet<MovieEdge> value : adjList.values()) {
        for(MovieEdge m : value){
            values.add(m);
        }
    }

    return values;
}

adjList.values()只是每个actor顶点

的MovieEdge类型的每个TreeSet

我的图表通过了我的所有教练JUNIT测试,所以我得到了这个任务的A,但这个bug是个人的事情,我必须知道WTF是错的。这让我疯了。谢谢。

我更新了,但仍然没有工作(给我同样的问题)上面的MovieEdge类代码

`@Override 
public int hashCode(){
    return source.getName().hashCode() + destination.getName().hashCode();
}

@Override
public boolean equals(Object obj){
    MovieEdge a = (MovieEdge)obj;
    if((source.getName().equals(a.source.getName())) && (destination.getName().equals(a.destination.getName()))){
        return true;
    }
    if((source.getName().equals(a.destination.getName())) && (destination.getName().equals(a.source.getName()))){
        return true;
    }
    return false;
}

@Override
public int compareTo(Object o) {
    MovieEdge a = (MovieEdge)o;

    if(movieName.equals(a.movieName)){
        if(equals(a))
            return 0;
        else if(source.getName().compareTo(a.source.getName()) < 0)
            return -1;
        else
            return 1;
    }else if(movieName.compareTo(a.movieName) < 0)
        return -1;

    return 1;
}    `

2 个答案:

答案 0 :(得分:1)

基本上,您的代码中有两个错误:

首先,实现Comparable,但不要覆盖equals()方法。 来自Comparable documentation

  

强烈建议(尽管不要求)自然   排序与平等一致。这是因为排序集   没有显式比较器的(和排序的地图)表现得“奇怪”   它们与自然顺序的元素(或键)一起使用   与equals不一致。特别是,这样的排序集(或排序的   map)违反了定义的集合(或映射)的一般合同   就平等方法而言。

如果movieName相等而不是sourcedestination,则您的Comparable会有一种奇怪的行为,因为它始终会返回1。这打破了compareTo contract

  

(x.compareTo(y)&gt; 0&amp; y.compareTo(z)&gt; 0)表示x.compareTo(z)&gt; 0

这会导致TreeSet无法正常工作,因此您的边缘会被添加两次。

编辑:据我所知,compareTo方法没有简单的实现。因此,我建议使用HashSet方便的hashCodeequals方法。 STH。像这样:

@Override
public int hashCode()
{
    final int prime = 31;
    int result = 1;
    result = prime * result + Objects.hashCode(movieName);
    result *= prime;
    // Only add, because order independent
    result += Objects.hashCode(destination);
    result += Objects.hashCode(source);
    return result;
}

@Override
public boolean equals(Object obj)
{
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    MovieEdge other = (MovieEdge)obj;
    if (Objects.equals(movieName, other.movieName))
    {
        if (Objects.equals(source, other.source)
                && Objects.equals(destination, other.destination))
        {
            return true;
        }
        if (Objects.equals(source, other.destination)
                && Objects.equals(destination, other.source))
        {
            return true;
        }
    }
    return false;
}

答案 1 :(得分:1)

Lumnitz提供的答案是正确的,我只想补充以下几点:

Set通过使用equals(Object obj)方法验证对象相等性来消除重复项,您没有覆盖MovieEdge类。

基本上,您需要了解以下几点:

(1)覆盖equals()hashcode()(来自java.lang.Object以检查对象是否相等(您的情况就是这样,即您想要消除两个{ {1}}使用MovieEdge等检查某些条件的对象。)

  

public boolean equals(Object obj):指示是否有其他对象   是“等于”这个。

(2)覆盖moviename(来自compareTo(Object o)进行排序(升序/降序)对象

  

public interface Comparable:此接口强加总排序   关于实现它的每个类的对象