Search code examples
javalinked-listclonesingly-linked-list

How to Clone a Circularly Linked List


I'm attempting to clone a circularly linked list in the same way you clone a singularly linked list, but am having trouble.

I've attempted leaving just the line calling the protected method of clone() in the public method clone(), but the program still throws the error.

public static void main(String[] args) throws CloneNotSupportedException 
  {

  CircularlyLinkedList<String> circularList = new 
  CircularlyLinkedList<String>();
  circularList.addFirst("1");
  circularList.addLast("2");
  circularList.addLast("3");
  circularList.addLast("4");


  CircularlyLinkedList<String> newList = new CircularlyLinkedList<String>();
  newList= circularList.clone();
  System.out.println(newList);
  }
@SuppressWarnings("unchecked")
public CircularlyLinkedList<E> clone() throws CloneNotSupportedException 
{
  // always use inherited Object.clone() to create the initial copy
    CircularlyLinkedList<E> other = (CircularlyLinkedList<E>) super.clone(); // safe cast
    if (size > 0) {                    // we need independent chain of nodes
        other.head = new Node<>(head.getElement(), null);
        Node<E> walk = head.getNext();      // walk through remainder of original list
        Node<E> otherTail = other.head;     // remember most recently created node
        while (walk != null) {              // make a new node storing same element
          Node<E> newest = new Node<>(walk.getElement(), null);
          otherTail.setNext(newest);     // link previous node to this one
          otherTail = newest;
          walk = walk.getNext();
        }
      }
    return other;

}

This code works while using singly linked lists. The expected output is the linked list printed twice, but the actual output is the exception thrown "CloneNotSupported". Note that the program doesn't throw this exception when clone() returns a null list.


Solution

  • Here is the problem, I think:

    CircularlyLinkedList<E> other = (CircularlyLinkedList<E>) super.clone(); 
    

    Now you haven't told us what the superclass of CircularlyLinkedList is, but the evidence is that:

    1. It does not implement the Cloneable marker interface.
    2. It does not override the clone method that it inherits from Object.

    With that combination, super.clone() will throw CloneNotSupportedException. This is explained in the javadoc.


    The real issue though is why are you calling super.clone()?

    If you are doing it because the superclass has state that needs to be copied in the clone you are creating, then it (the superclass) must provide some way of cloning itself; i.e. it needs to do one of the above ... or provide a "copy constructor" or similar.

    If you are doing it just to make the typing work, then you should probably do something like this:

        CircularlyLinkedList<E> other = new CircularlyLinkedList<>(); 
    

    where the constructor (which can private if necessary) creates an instance that you can start filling in. Note that this is type-safe.


    I note this comment:

    // always use inherited Object.clone() to create the initial copy
    

    If it is intended to mean always for this class, then just fix it to match what you actually do. Bearing in mind that you can only if the superclass is cloneable ... which it currently isn't!

    If it is intended to record that it is "best practice" (or something; see this) to do this in all cases, that is simply wrong:

    • As I explained, you can't do it in all cases.
    • While there are arguments that it may be inadvisable to use another method to duplicate superclass state, a subclass is entitled to make assumptions about its superclass in OO design.
    • And besides, by calling super.clone() you are making an assumption ... that clone() will work!