3

Here is the code for my Element class:

public class Element<T> {
    Element<T> next;
    Element<T> previous;
    T info;
}

... and for CircularList:

public class CircularList<T> {
    Element<T> first=null;

    public void add(Element<T> e){
        if (first==null){
            first=e;
            e.next=e;
            e.previous=e;
        } 
        else { //add to the end;
            first.previous.next=e;
            e.previous = first.previous;
            first.previous=e;
            e.next=first;
        }
    }

    public boolean equals(Object o){
        // TODO
    }
}

I don't know how to make an equals method for a circular list...

Only check if o instanceof CircularList<T> ?? (if ! return false else true?) mmm.. dunno how to do it :(

I know (from school) that I have to check 3 conditions:

if this == o return true
if o == null return false
if !o instanceof CircularList<T> return false

...

I don't know now if I have to check element from list1 to o , with a while... help me :)

Is an university test and I cannot modify or add method to element class.. I only have to implement the equals method from the CircularList<T> class!

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
  • Not clear at all what you are asking..!! – Vimal Bera Aug 06 '14 at 09:02
  • 1
    Can you post all the code you tried? – Wundwin Born Aug 06 '14 at 09:03
  • 1
    share your all code!! – Zafer Celaloglu Aug 06 '14 at 09:03
  • This link may help you http://stackoverflow.com/questions/16382887/java-equals-method-using-generics-and-instanceof – Wundwin Born Aug 06 '14 at 09:07
  • hmm circular list is tricky . because start and end are kind of unknown, I like to see an answer for this too. – nafas Aug 06 '14 at 09:09
  • We usually prefer to have code (and any text data in general) to be given to us as **text**. Text is lighter and can be easily copied/indexed and manipulated (the syntax can be coloured for example). I'd suggest you to simply copy and paste your code in your message and try formatting it. I'd of course gladly help with this if it's an issue for you :) – ccjmne Aug 06 '14 at 09:40

6 Answers6

3

I think the key point is that the lists are circular. Thus, I assume that the lists

A, B, C, D, E 
   B, C, D, E, A

should be considered as equal. Because they are. When you rotate a circle, it's still a circle.

So I guess the trick is to check each element of the other list, and see whether the the list is equal when starting at this element. In the example above, one would test

  • Is the list B, C, D, E, A equal to A, B, C, D, E? No
  • Is the list C, D, E, A, B equal to A, B, C, D, E? No
  • Is the list D, E, A, B, C equal to A, B, C, D, E? No
  • Is the list E, A, B, C, D equal to A, B, C, D, E? No
  • Is the list A, B, C, D, E equal to A, B, C, D, E? Yes. Finally.

A quick implementation with some test cases, I hope that the most important cases are covered:

public class CircularListEquality {
    public static void main(String[] args) {

        CircularList<String> c0 = createList("A", "B", "C", "D", "E");
        CircularList<String> c1 = createList("A", "B", "C", "D");
        CircularList<String> c2 = createList(     "B", "C", "D", "E");
        CircularList<String> c3 = createList(     "B", "C", "D", "E", "A");

        CircularList<String> c4 = createList("A", "B", "C", "A", "B", "C");
        CircularList<String> c5 = createList("A", "B", "C");

        CircularList<String> c6 = createList("A");
        CircularList<String> c7 = createList("A", "A", "B", "C");

        test(c0, c1, false);
        test(c0, c2, false);
        test(c1, c2, false);
        test(c0, c3, true);
        test(c3, c0, true);
        test(c4, c5, false);
        test(c5, c4, false);
        test(c6, c7, false);
    }

    private static <T> void test(
        CircularList<T> c0, CircularList<T> c1, boolean expected) {
        boolean actual = c0.equals(c1);
        if (actual == expected) {
            System.out.print("PASSED");
        } else {
            System.out.print("FAILED");
        }
        System.out.println(" for " + toString(c0) + " and " + toString(c1));
    }

    private static <T> String toString(CircularList<T> c) {
        StringBuilder sb = new StringBuilder();
        Element<T> current = c.first;
        while (true) {
            sb.append(String.valueOf(current.info));
            current = current.next;
            if (current == c.first) {
                break;
            } else {
                sb.append(", ");
            }
        }
        return sb.toString();
    }

    private static CircularList<String> createList(String... elements) {
        CircularList<String> c = new CircularList<String>();
        for (String e : elements) {
            c.add(createElement(e));
        }
        return c;
    }

    private static <T> Element<T> createElement(T t) {
        Element<T> e = new Element<T>();
        e.info = t;
        return e;
    }
}

class Element<T> {
    Element<T> next;
    Element<T> previous;
    T info;
}

class CircularList<T> {
    Element<T> first = null;

    public void add(Element<T> e) {
        if (first == null) {
            first = e;
            e.next = e;
            e.previous = e;
        } else { // add to the end;
            first.previous.next = e;
            e.previous = first.previous;
            first.previous = e;
            e.next = first;
        }
    }

    public boolean equals(Object object) {
        if (this == object)
        {
            return true;
        }
        if (object == null)
        {
            return false;
        }
        if (!(object instanceof CircularList<?>))
        {
            return false;
        }
        CircularList<?> that = (CircularList<?>) object;

        Element<?> first0 = first;
        Element<?> current0 = first0;
        Element<?> first1 = that.first;
        Element<?> current1 = first1;
        while (true) {
            if (equalSequence(current0, current0, current1, current1)) {
                return true;
            }
            current1 = current1.next;
            if (current1 == first1) {
                return false;
            }
        }
    }

    private static boolean equalSequence(
        Element<?> first0, Element<?> current0,
        Element<?> first1, Element<?> current1) {
        while (true) {
            if (!equalElements(current0, current1)) {
                return false;
            }
            current0 = current0.next;
            current1 = current1.next;
            if (current0 == first0 && current1 == first1) {
                return true;
            }
            if (current0 == first0) {
                return false;
            }
            if (current1 == first1) {
                return false;
            }
        }
    }

    private static boolean equalElements(Element<?> t0, Element<?> t1) {
        if (t0 == null) {
            return t1 == null;
        }
        return equal(t0.info, t1.info);
    }

    private static <T> boolean equal(T t0, T t1) {
        if (t0 == null) {
            return t1 == null;
        }
        return t0.equals(t1);
    }
}

(EDIT: Added another test case)

Marco13
  • 53,703
  • 9
  • 80
  • 159
  • Wow! That's quite thorough. But it's actually broken a little. Let's consider two lists: the first one has only one element, `e1`. Would the second one have `e1`, `e1`, `e2` and `e3`, your algorithm would still return `true` :) However, you're probably right and `e1`, `e2` is most likely supposed to "equal" `e2`, `e1`, you made a very good point. – ccjmne Aug 06 '14 at 10:34
  • @ccjmne I added another test case for this, and it works as expected. Note that the `equalSequence` method implicitly checks whether the sequences have the same length, by returning `true` only when the sequences arrive again at their first element *at the same time*. – Marco13 Aug 06 '14 at 10:38
  • Well, the elements are *specific* for the list. You can' use the **same** element in *two* lists, because then, the `next` and `previous` pointers are simply inconsistent. That's not related to my solution, but to the way how the list is implemented in the first place. – Marco13 Aug 06 '14 at 10:48
  • Oh my, you're right! I can't even think with mutable objects anymore... +1 :) – ccjmne Aug 06 '14 at 10:49
2

The solution is obtained by tweaking your CircularList class a little: let's add it a length field.

public class CircularList<T> {
    int length = 0;  // <- this field contains the number of elements that the list has.
    Element<T> first = null;

    public void add(Element<T> e){
        if (first == null){
            first = e;
            e.next = e;
            e.previous = e;
        } 
        else {
            first.previous.next = e;
            e.previous = first.previous;
            first.previous = e;
            e.next = first;
        }

        this.length++;  // <- increment each time you call add().
    }
}

Now, implementing equals becomes trivial:

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;

    @SuppressWarnings("unchecked")
    final CircularList<T> other = (CircularList<T>) obj;

    if(other.length != this.length) {
        return false;
    }

    Element<T> current = this.first;
    Element<T> otherCurrent = other.first;
    int offset = 0;
    boolean found = false;
    do {
        found = checkSequence(current, otherCurrent);
        if(!found) {
            offset++;
            otherCurrent = otherCurrent.next;
        }
    } while(!found && offset < length) ;

    return found;
}

private boolean checkSequence(Element<T> current, Element<T> otherCurrent) {
    int i = 0;
    while(i < length && current.info == otherCurrent.info) {
        current = current.next;
        otherCurrent = otherCurrent.next;
        i++;
    }

    return i == length;
}

What I'm doing here in the checkSequence method is simply iterating over each elements (there are length of them in both lists) and checking that they're all the same by pair.

Then, if I left my loop because i reached length, it means that I've been through all the elements and that they were all identical. If i, at this point, is smaller than length, it means that the ith elements of the lists weren't the same.

Thus, I simply return i == length.


On top of this, I want to handle the cases where we're considering lists like:

  • A, B, C
  • C, A, B

... which must be equal.

Thus, I'm simply calling checkSequence, at most length times, comparing the second list to the first one with all possibles offsets.

ccjmne
  • 9,333
  • 3
  • 47
  • 62
  • This is perfect if we can modify the class. Can you comment on my latest answer? I tried to use ref to check for loop break. – Maas Aug 06 '14 at 10:19
  • You're right, it implies being able to alter the `CircularList` class a little! However, it would also cover the cases where the list contains duplicate elements... ;) – ccjmne Aug 06 '14 at 10:21
  • In this case, the lists `A,B,C` and `B,C,A` would be considered as being different, but I think they should be equal (because they are just rotated). – Marco13 Aug 06 '14 at 10:34
  • You're probably right! That's a very good point. I now also handle that case. – ccjmne Aug 06 '14 at 11:48
0

The ! operator just inverts the true to false and vice versa.

So, the code of yours about that one thing would mean an opposite of the actual

if !o instanceof CircularList<T>

would return false, if the o object is instance of CircularList<T>. Otherwise it would return to be true if the o is not an instance of CirculatList<T>. You're just trying to invert the actual result of the o instanceof CircularList<T>.

Afzaal Ahmad Zeeshan
  • 15,669
  • 12
  • 55
  • 103
0

How about this equals implementation? We can avoid tweaking your Class.

    public boolean equals(CircularList<T> other)
        {
           **Standard checks will go here (ref equals, class type etc)**

            boolean result = false;
            result = first.equals(other.first);

            Element<T> next = first.next;
            Element<T> nextOfOther = other.first.next;

            while (null != next && null != nextOfOther)
            {
                // break cyclic loop
                if (next.equals(first))
                    break;

                result = next.equals(nextOfOther);

                if (!result)
                    // We know they aren't equal so break
                    break;

                next = next.next;
                nextOfOther = nextOfOther.next;

            }

            return result;
        }
Maas
  • 1,317
  • 9
  • 20
  • 1
    I see a `StackOverflowException` coming right away... :) – ccjmne Aug 06 '14 at 09:19
  • I did mention about NULL checks to stop it, Would be happy to learn, how! :) – Maas Aug 06 '14 at 09:20
  • I must (because it's an university test) to implement the equals method only to the CircularList class... but i don't know how to do it! – Marco Baratella Aug 06 '14 at 09:22
  • 2
    I think that since his `List` is **circular**, every element has a next element set. Consider the list that has only one element: its `next` and its `prev` both point to itself! Nowhere is a `null` reference (if I'm correct). – ccjmne Aug 06 '14 at 09:22
  • Err, that was off my mind! Thanks... Then he can always have "first" element of List as the ref point to break. Let me try to code it and paste – Maas Aug 06 '14 at 09:27
  • 1
    You're right. Except that the List itself would have that information, while you were suggesting to implement the recursive method in the Element class :) I think that you wouldn't have access to that bit of information you need. Besides that, even if the list wasn't circular, considering a list that has two elements, we'd still never end: while checking for the first ones' equality, you'd check whether its next are the same. This includes checking if the second one's **previous**' are the same... we're back at the beginning, only 2 stacks deeper :p – ccjmne Aug 06 '14 at 09:27
  • 1
    Same issue as @user3883676's answer. let's consider two lists: the first one has only one element, `e1`. Would the second one have `e1`, `e1`, `e2` and `e3`, your algorithm would still return `true` :) – ccjmne Aug 06 '14 at 10:19
0

The instruction :this == o; returns true only when they have the same adresse (when it's the same object), I think that what you need is something that would return true when both lists contains same elements, try this :

public boolean equals(CircularList<T> other){
    Element<T> e1 = first;
    Element<T> e2 = other.first;
    while(e1.next != first && e2.next != other.first){
       if(e1.info == e2.info){
           e1 = e1.next;
           e2 = e2.next;
       }
       else return false;
    } 
    if (e1.next == first && e2.next == other.first)
         return true;
    else return false;

}

i think that this would work !!

Othman Benchekroun
  • 1,998
  • 2
  • 17
  • 36
  • Ty for all, very interisting..but I must (because it's an university test) to implement the equals method only to the CircularList class.. and i cannot edit or add any other method to element class! – Marco Baratella Aug 06 '14 at 09:38
  • Actually, this would probably give you a `StackOverFlowException` since the list is circular, I think you may need to add an attribute for the length of the list – Othman Benchekroun Aug 06 '14 at 09:43
  • ok i see, is it possible to add an attribute to your class for the length of the list ? – Othman Benchekroun Aug 06 '14 at 09:48
  • i can only add something inside the equals method... :) – Marco Baratella Aug 06 '14 at 09:50
  • This is not as simple as you wanted it, but i don't see how you can do it othewise – Othman Benchekroun Aug 06 '14 at 09:57
  • This doesn't work either. Consider two lists: the first one has only one element, let's say `e1`. Considering a second list which would have `e1`, `e1`, `e2`, `e3`, your algorithm would still return `true` :) – ccjmne Aug 06 '14 at 10:04
  • Look at the condition of the loop, considering your lists, the loop would stop since e1.next == e1 which is the first element, the condition `e1.next == first and e2.next == other.first` is also false, so it would return false – Othman Benchekroun Aug 06 '14 at 10:11
  • I think that you're slightly confused. Try it yourself! Just run this piece of code: Element e1 = new Element(); e1.info = "1"; Element e2 = new Element(); e2.info = "2"; CircularList l1 = new CircularList<>(); l1.add(e1); CircularList l2 = new CircularList<>(); l2.add(e1); l2.add(e1); l2.add(e2); System.out.println(l1.equals2(l2)); – ccjmne Aug 06 '14 at 10:13
  • try it too, i'm pretty sure it's working , look at the method `add(Element e)`, for the first list, e1 is the first, and also the previous and the next of the first. so the loop would not run, and this would definitely return false. – Othman Benchekroun Aug 06 '14 at 10:29
  • Look at Marco13's solution, it's the same algorithm. – Othman Benchekroun Aug 06 '14 at 10:38
0

How about this, here I will first find and check for length then will check the elements.

public boolean equals(CircularList<T> other)
    {
        boolean result = false;
        result = first.equals(other.first);

        if (!result)
            return result;

        Element<T> next = first.next;
        Element<T> nextOfOther = other.first.next;

        int firstCount = 1;

        while (null != next)
        {
            if (next.equals(first))
                break;

            firstCount++;
            next = next.next;
        }

        int secondCount = 1;

        while (null != nextOfOther)
        {
            if (nextOfOther.equals(other.first))
                break;

            secondCount++;
            nextOfOther = nextOfOther.next;
        }

        if (firstCount != secondCount)
        {
            result = false;
        }

        next = first.next;
        nextOfOther = other.first.next;

        while (firstCount > 1)
        {
            result = next.equals(nextOfOther);
            if (!result)
                // We know they aren't equal so break
                break;

            next = next.next;
            nextOfOther = nextOfOther.next;
            firstCount--;

        }

        return result;
    }
Maas
  • 1,317
  • 9
  • 20
  • Same here as for ccjmne: Shouldn't two **circular** lists `A,B,C` and `B,C,A` be considered as being equal? – Marco13 Aug 06 '14 at 10:40
  • I think its arguable, consider a method to process the list if its only in the given order. Because we are holding the "first" element and any operation would start from there. – Maas Aug 06 '14 at 10:42
  • Only the asker can clarify this. (And maybe not even him, but only his advisor ;-)). But an equality test for simple linked lists would be rather trivial, so I guess there's more behind this... – Marco13 Aug 06 '14 at 10:45
  • Totally agree :) Java LinkList doesn't even have any special equals and rather use that of parent. java.util.AbstractList.equals(Object) – Maas Aug 06 '14 at 10:46