简体   繁体   中英

Comparing circular linkedlists equals method

public class LinkedList {

 Object contents;
 LinkedList next = null;

public boolean equals(Object item) {
   return (this == item) || ((item instanceof LinkedList) &&  this.equals((LinkedList)item)); 
  }

 public boolean equals(LinkedList item) { 
   return myUtil.equals(this.contents, item.contents) && myUtil.equals(this.next, item.next); 
 }

} 

public class myUtil{
  public static boolean equals(Object x, Object y) {
    return (x == y) || (x != null && x.equals(y));
 }
}

main(){
 LinkedList myList = new LinkedList();
 myList.next = new LinkedList();
 LinkedList head = myList.next;
 myList.next = head;
}

I think i have created a circular linkedlist here. So what i have done is to overwrite the equals method to ensure that circular references are handled:

For some reason the LinkedList.equals doesnt seem to return...is it because of my circular linkedlist, or am i missing some conditions?

The primary problem with this code is that your comparison will not terminate upon circular reference, and will loop forever if all contents fields are equal. It will always continue to the next comparison, and since the next item is always there (as it's a circle) this will continue forever.

myUtil.equals(this.contents, item.contents) && myUtil.equals(this.next, item.next);

To solve this issue, the simplest method would be to add a boolean private 'visited' field to each List item. When you compare, set visited on each item after the comparison. If both are not visited and the same, then continue. If only one is visited, your lists are not identical. If both are visited, you've compared the reachable entirety of the list. Generally, having loops in your list are a bad idea, and there exist algorithms specifically to detect them. This can be a confusing topic. Here is a coverage of loop detection that may help you understand the issue further. Remember, if you use the visited field, you must unset all of them with another loop in your equals() to allow it to run again.

On another note, you do not initialize the contents field of your list nodes for the test. This is okay here, since they are initialized to null, but generally it is good practice to explicitly initialize all your fields.

Generally speaking, you also don't need the equals(Object item) override. Try

public boolean equals(LinkedList item){
  if (this == item){
     return true; // It's the same object
  }

  // Add some null checks here, I'm lazy

  if (this.visited && item.visited && this.contents.equals(item.contents){
     this.visited = false; //Unset
     item.visited = false;
     return true;
  }
  if (this.visited && !item.visited){
      this.visited = false;
      return false;
  }
  if (!this.visited && item.visited){
      item.visited = false;
      return false;
  }
  if (!this.visited && !item.visited && this.visited.contents.equals(item.contents){
      this.visited = true;
      item.visited = true;
      boolean ret = this.next.equals(item.next);
      this.visited = false;
      item.visited = false;
      return ret;
  }

  // Contents not equal
  return false;
}

This backtracks and unsets with some basic recursion. I obviously haven't compiled this, but that's the gist of it, I think (I hope there aren't too many errors)

Two issues, first you do not have a circular linked list. The follow code creates 2 lists, list1.next = list2, list2.next = null. No circle created.

LinkedList myList = new LinkedList();
myList.next = new LinkedList();
LinkedList head = myList.next;
myList.next = head;

Second, if you DID have a circular linked list, the following would produce an infinite loop since there is no end condition reached this is because in a circular linked linked, next should never be null .

public boolean equals(Object item) {
  return (this == item) || ((item instanceof LinkedList) &&       
    this.equals((LinkedList)item)); 
}

public boolean equals(LinkedList item) { 
   return myUtil.equals(this.contents, item.contents) && myUtil.equals(this.next, item.next); 
}

To do this effectively you need to provide SOME mechanism to iterate the list in a non-circular fashion even if this mechanism is private and not exposed to other users. One way to do this would be to mark a single node as the "root".

return myUtil.equals(this.contents, item.contents) 
&& myUtil.equals(this.next, item.next); 

I would imagine that this is your issue as you suspected, when you perform the second expression of the && namely myUtil.equals(this.next, item.next); you enter the myUtil.equals method which performs this line:

return (x == y) || (x != null && x.equals(y));

Which in turn uses x 's .equals() method, which will repeat the process for its item.next , and so on and so forth since you have a circularly linked list.

This will cause an infinite loop, this is because in the code:

public static boolean equals(Object x, Object y) {
        return (x == y) || (x != null && x.equals(y));
    }

The x.equals(y) will again invoke:

public boolean equals(LinkedList item) {
        return myUtil.equals(this.contents, item.contents)
                && myUtil.equals(this.next, item.next);
    }

But if you are performing myList1.equals(myList1) , you will not get an infinite loop because the (x==y) in myUtils.equals() will return true so infinite loop will not happen if you compare same objects.

However when you compare different objects, you will enter into an infinite loop.

This is not a circular list issue, this is because of the code design you've chosen.

Finally completed my equals method implementation. For this I had to use additional checking tools by myself. I can't tell it is effective, but some extraordinary states are checked.

public boolean equals(Object o)
{
    if(!(o instanceof CircularlyLinkedList))
        return false;

    CircularlyLinkedList<E> list=(CircularlyLinkedList<E>)o;

    if(this==list)
        return true;

    if(size()!=list.size())
        return false;
    //tail element of this object
    Node<E> thisTail=tail;

    //tail element of list passing as parameter
    Node<E> listTail=list.tail;

    //checking if tail elements of both lists are the same or not. If not rotate list till equatation is provided for tails
    if(!thisTail.equals(listTail))
    {
        listTail = equate(list);
        if(listTail==null)
            return false;
    }

    //Each element checking
    for(int i=0; i<size(); i++)
    {
        thisTail=thisTail.next;
        listTail=listTail.next;

        if(!thisTail.equals(listTail))
        {
            listTail = equate(list);
            listTail=tail;
            i=0;
            if(listTail==null)
                return false;
        }
    }

    return true;
}

And equate method:

private Node<E> equate(CircularlyLinkedList<E> list)
{

    Node<E> thisTail=tail;
    Node<E> listTail;
    for(int i=0; i<list.size(); i++)
    {
        list.rotate();
        listTail=list.tail;

        //If full rotation completes then returns null
        if(list.getRotation()==0)
        {
            return null;
        }
        if(thisTail.equals(listTail))
        {
            return nodeList;
        }
    }

    return null;
}

getRotation method returns count of rotation operation and changes between 0 and size-1 . I hope that it will become useful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM