Trouble understanding constructors and reasons for calling them in another class

116 Views Asked by At

I'm studying software engineering at university, and I generally have had a pretty firm grasp on the fundamental concepts of Object Oriented Programming, but lately I've found myself falling behind on some of the concepts that aren't as easy to understand.

One major issue is that I can't get my head around class constructors; something that I know will be my downfall if I don't nip this in the bud soon.

I've asked my tutor to explain it, but it must be the way they explained it that just didn't cause me to have the "ah-hah" moment I usually do.

To help you help me, see the below example, working program (demonstrating the use and manipulation of a linked list):

Main class:

package root;

public class Node<E> {
    private E nodeValue;
    private Node<E> next;

    public static void main (String[] args) {
        try {
            // Example 1: Create an empty list and print it. 
            SinglyLinkedList<Integer> list1 = new SinglyLinkedList<Integer>();
            System.out.println("Example 1: Create an empty list.");
            System.out.println(list1.printList());

            // ----------------------------------------------------------
            // Example 2: Create a list of 1 integer (1) using InsertNodeToTail.  
            System.out.println("\nExample 2: Create a list of 1 integer using InsertNodeToTail.");
            SinglyLinkedList<Integer> list2 = new SinglyLinkedList<Integer>();
            System.out.println("Before: " + list2.printList());
            list2.insertNodeToTail(1);
            System.out.println("After:  " + list2.printList());

            // ----------------------------------------------------------
            // Example 3: Create a list of 1 integer (1) using InsertNodeToHead.  
            System.out.println("\nExample 3: Create a list of 1 integer using InsertNodeToHead.");
            SinglyLinkedList list3 = new SinglyLinkedList();
            System.out.println("Before: " + list3.printList());
            list3.insertNodeToHead(1);
            System.out.println("After:  " + list3.printList());

            // ----------------------------------------------------------
            // Example 4: Create a list of 5 integers (1, 3, 5, 7, and 9) 
            // using InsertNodeToTail. Output: 1->3->5->7->9
            System.out.println("\nExample 4: Create list 1->3->5->7->9 using InsertNodeToTail.");
            // Create an array of 5 integers
            int[] array4 = { 1, 3, 5, 7, 9 };
            // Create the head node 
             SinglyLinkedList<Integer> list4 = new SinglyLinkedList<Integer>();
            System.out.println("Before: " + list4.printList());
            // Insert nodes
            for (int i = 0; i < array4.length; i++)
                list4.insertNodeToTail(array4[i]);
            System.out.println("After:  " + list4.printList());

            // ----------------------------------------------------------
            // Example 5: Create a list of 5 integers (1, 3, 5, 7, and 9) 
            // using InsertNodeToHead. Output: 1->3->5->7->9
            System.out.println("\nExample 5: Create list 1->3->5->7->9 using InsertNodeToHead.");
            // Create an array of 5 integers
            int[] array5 = { 1, 3, 5, 7, 9 };
            // Create the head node 
             SinglyLinkedList<Integer> list5 = new SinglyLinkedList<Integer>();
            System.out.println("Before: " + list5.printList());
            // Insert nodes
            for (int i = array5.length - 1; i >= 0; i--)
                list5.insertNodeToHead(array5[i]);
            System.out.println("After:  " + list5.printList());

            // ----------------------------------------------------------
            // Example 6: Insert new node before a current node
             System.out.println("\nExample 6: Insert node 0 before node 1.");
            // Use list2, insert node 0 before node 1
            System.out.println("Before: " + list2.printList());
            list2.insertNodeBefore(0, 1);
            System.out.println("After:  " + list2.printList());

            // ----------------------------------------------------------
            // Example 7: Insert new node before a current node
            System.out.println("\nExample 7: Insert node 4 before node 5.");
            // Use list4, insert node 4 before node 5
            System.out.println("Before: " + list4.printList());
            list4.insertNodeBefore(4, 5);
            System.out.println("After:  " + list4.printList());

            // ----------------------------------------------------------
            // Example 8: Insert new node after a current node
            System.out.println("\nExample 8: Insert node 2 after node 1.");
            // Use list2, insert node 2 after node 1
            System.out.println("Before: " + list2.printList());
            list2.insertNodeAfter(2, 1);
            System.out.println("After:  " + list2.printList());

            // ----------------------------------------------------------
            // Example 9: Insert new node after a current node
            System.out.println("\nExample 9: Insert node 10 after node 9.");
            // Use list4, insert node 10 after node 9
            System.out.println("Before: " + list4.printList());
            list4.insertNodeAfter(10, 9);
            System.out.println("After:  " + list4.printList());

            // ----------------------------------------------------------
            // Example 10: Remove node if node value is given
            System.out.println("\nExample 10: Remove node 10.");
            // Use list4, remove node 10
            System.out.println("Before: " + list4.printList());
            list4.remove(10);
            System.out.println("After:  " + list4.printList());

            // ----------------------------------------------------------
            // Example 11: Remove node that is not in the list
            System.out.println("\nExample 11: Remove node 100.");
            // Use list4, remove node 100
            System.out.println("Before: " + list4.printList());
            list4.remove(100);
            System.out.println("After:  " + list4.printList());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public Node() { 

    }

    public Node(E nVal) { 
        nodeValue = nVal; 
    }

    public Node(E nVal, Node<E> nextNode) {
        nodeValue = nVal;
        next = nextNode;
    }

    public E getNodeValue() {
        return nodeValue;
    }

    public void setNodeValue (E nVal) {
        nodeValue = nVal;
    }

    public Node<E> getNext() {
        return next;
    }

    public void setNext (Node<E> n) {
        next = n;
    }
}

Sub-class:

package root;

import java.io.*;

public class SinglyLinkedList<E> {

    private Node<E> head;

    // Create an empty list 
    public SinglyLinkedList() {
        head = null;
    }

    // Access to the entire linked list (read only)
    public Node<E> getHead() {
        return head;
    }

    // Insert a node with node value = nVal as the last node
    public void insertNodeToTail(E nVal) {
        Node<E> lastNode = new Node<E>();
        lastNode.setNodeValue(nVal);
        if (head == null) {
            head = lastNode;
            return;
        }

        Node<E> curr = head;
        while (curr.getNext() != null) {
            curr = curr.getNext();
        }
        curr.setNext(lastNode);
    }

    // Insert a node with node value = nval as the first node
    public void insertNodeToHead(E nVal) {
        Node<E> newHead = new Node<E>();
        newHead.setNodeValue(nVal);
        newHead.setNext(head);
        head = newHead;
    }

    // Insert new node nVal to the list before current node curVal 
    public void insertNodeBefore(E nVal, E curVal) {
        Node<E> newNode = new Node<E>(nVal);

        Node<E> curr = head;
        Node<E> prev = null;

        if (head.getNodeValue() == curVal) {
            newNode.setNext(head);
            head = newNode;
            return;
        }

        // scan until locate node or come to end of list
        while (curr != null) {
            // have a match 
            if (curr.getNodeValue() == curVal) {
                // insert node
                newNode.setNext(curr);
                prev.setNext(newNode);
                break;
            } else {
                // advanced curr and prev
                prev = curr;
                curr = curr.getNext();
            }
        }
    }

    // Insert new node nVal to the list after current node curVal 
    public void insertNodeAfter(E nVal, E curVal) {
        Node<E> newNode = new Node<E>();
        newNode.setNodeValue(nVal);

        Node<E> curr = head.getNext();
        Node<E> prev = head;

        //scan until locate a node or come to the end of the list
        while (prev != null) {
            //have a match
            if (prev.getNodeValue().equals(curVal)) {
                //insert node
                newNode.setNext(curr);
                prev.setNext(newNode);
                break;
            } else {
                //advance curr and prev
                prev = curr;
                curr = curr.getNext();
            }
        }
    }

    // Remove the node containing item nVal
    public void remove(E nVal) throws IOException {
        if (head == null) {
            throw new IOException("List empty!");
        } else {

            Node<E> curr = head;
            Node<E> prev = null;

            // becomes true if we locate target
            boolean foundItem = false;
            // scan until locate nodeVal or come to end of list

            while (curr != null && !foundItem) {
                // have a match 
                if (curr.getNodeValue() == nVal) {
                    // if current node is the first node
                    // remove first node by moving head to next node
                    if (prev == null) {
                        head = head.getNext();
                    } else { // erase intermediate node
                        prev.setNext(curr.getNext());
                    }
                    foundItem = true;
                } else {
                    // advanced curr and prev
                    prev = curr;
                    curr = curr.getNext();
                }
            }
        }
    }

    public String printList() {
        String outputList = "";
        Node<E> temp = head;

        if (temp == null) {
            return "List empty!";
        }        
        do {
            // Print head node value
            outputList += temp.getNodeValue().toString();
            // Move to next node
            temp = temp.getNext();
            // if next node is not empty, print -> 
            // else print end of line then break the loop
            if (temp != null) {
                outputList += "->";
            } else {
                break;
            }
        } while (true);
            // the loop terminates itself when it reaches to 
            // end of the list
            return outputList;
    }
}

Can anyone explain what the purpose of the constructors in the generic Node<E> (Main) class are? In what situation(s) should they be called upon?

3

There are 3 best solutions below

0
On BEST ANSWER

You have three constructors:

public Node() { 

}

public Node(E nVal) { 
    nodeValue = nVal; 
}

public Node(E nVal, Node<E> nextNode) {
    nodeValue = nVal;
    next = nextNode;
}

The first one is the default constructor, taking no arguments. It instantiates an Object of Class Node.

The second one takes argument (E nVal); nVal is of Type E, therefore the instantiated Node-Object will be of Type Node<nVal>.

The third constructor takes two arguments(E nVal, Node nextNode); it does the same as the second constructor plus it sets the next Node in the list, nextNode; this reference is then stored in next variable of the instantiated Object.

0
On

You can call public Node() { if you want to create a new Node and do not have any data.

public Node(E nVal) { can be called if you wand to create a new node with a value.

Node(E nVal, Node<E> nextNode) is called, if you have nodeValue and nextNode

0
On

Each constructor creates a new object instance. The default constructor does not take any arguments, and if you don't supply any of your own code also does nothing to the new object.

For convenience one usually creates constructors with arguments and/or with code inside the constructor, so that the returned new object instance is pre-configured with some data.

This is especially useful if, for example, the data will never change after construction. Another use case is that any object instance you will ever get is adhering to certain rules - which you can only safely do by taking care of these rules inside the constructor, before giving the object instance away to be used.