Java: Dual-chained list (deck) implementation with callback, Generics, Exception and lambda

Asked

Viewed 746 times

0

I’m doing the implementation in java, however I’m having some problems with how to integrate Generics and lambda in the deck:

Deque.java:

import java.util.ArrayList;
public class Deque {
    private Element head;
    private Element tail; 
    public Deque() {
        head = tail = null;
    }
    public boolean isEmpty() {
        return head == null && tail == null;
    }
    public boolean isFull() {
        return false;
    }
    public ArrayList<Integer> List() throws EmptyListException { //generics and exception
        ArrayList<Integer> list = new ArrayList<Integer>();     
        if(head == null)
            throw new EmptyListException("The list is empty!");     
        else{
            Element elementFirst = getFirst();          
            while(elementFirst != null){
                int value = elementFirst.getValue(); 
                list.add(value);
                elementFirst = elementFirst.getNext();
            }
            return list;
        }       
    }
    public void addFirst(Object o) {
        System.out.println("Addited in first: " + o);
        Element e = new Element(o);
        e.setNext(head); 

        if(head != null) {
            head.setPrev(e);  
        }
        head = e; 
        if(tail == null) {
            tail = e; 
        }
    }
    public void addLast(Object o) throws Exception {
        System.out.println("Addited in last: " + o);
        Element e = new Element(o);
        e.setPrev(tail); 

        if(tail != null) {
            tail.setNext(e);
        }
        tail = e;
        if(head == null) {
            head = e;
        }
    }
    public Object removeFirst() throws Exception {
        if(this.isEmpty()) {
            throw new Exception ("The Queue is empty!");
        } else {
            Object o = head.getValue();
            head = head.getNext(); 
            if(head == null) { 
                tail = null;
            } else {
                head.setPrev(null); 
            }
            System.out.println("Removed in first: " + o);
            System.out.println("New head: " + head.getValue());
            return o;
        }
    }
    public Object removeLast() throws Exception {
        if(this.isEmpty()) {
            throw new Exception ("The Queue is empty!");
        } else {
            Object o = tail.getValue();
            tail = tail.getPrev(); 
            if(tail == null) {
                head = null;
            } else {
                tail.setNext(null); 
            }
            System.out.println("Removed in last: " + o);
            System.out.println("New tail: " + tail.getValue());
            return o;
        }
    }
    public void clear() {
        System.out.println("");
        System.out.println("Start clear... ");
        while (!this.isEmpty()) {
            try {
                this.removeFirst();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("End clear");
        System.out.println("");
    }
    public void listing() {
        Element e = head;
        System.out.println("");
        System.out.println("Start listing... ");
        while (e != null) {
            System.out.println("Value: " + e.getValue());
            e = e.getNext();
        }
        System.out.println("End listing");
        System.out.println("");
    }
}

Element.java:

public class Element {
    private Element next;
    private Element prev;
    private Object value;
    public Element (Object v) {
        value = v;
    }
    public void setNext(Element e) {
        this.next = e;
    }
    public Element getNext() {
        return next;
    }
    public void setPrev(Element e) {
        this.prev = e;
    }
    public Element getPrev() {
        return prev;
    }
    public void setValue(Object value) {
        this.value = value;
    }
    public Object getValue() {
        if(value != null) {
            System.out.println("the value is null!");
            return false;
        }
        return value;
    }
}

Main.java:

public class Main {
    public static void main(String[] args) throws Exception { 
        Deque deque = new Deque(); 

        do{         
            System.out.println("Select the option\n 1->Add first:\n 2->Add last:\n3->List:\n 4->Remove first:\n 5->Remove last:\n 6->Clear:\n 7->Exit:\n");     
            option = scanner.next();            
            if(option == 1){
                System.out.println("Enter with the value: ");
                value = scanner.next();
                deque.addFirst(value);
            }               
            else if(option == 2){
                System.out.println("Enter with the value: ");
                value = scanner.next();
                deque.addLast(value);       
            }                   
            else if(option == 3){
                ArrayList<String> myList = new ArrayList<String>();
                try {
                    myList = deque.List();
                    //deque.listing();
                } 
                catch (EmptyListException e) {
                    e.printStackTrace();                    
                }
            }
            else if(option == 4){
                deque.removeFirst();        
            }
            else if(option == 5){
                deque.removeLast(); 
            }
            else if(option == 6){
                deque.clear();
            }
            else {
                System.out.println("Invalid option");
            }
        }
        while(option != "7");
    }
}

1 answer

6


Let’s start with the Element:

public class Element<T> {
    private Element<T> next;
    private Element<T> prev;
    private T value;

    public Element(T v) {
        value = v;
    }

    public void setNext(Element<T> e) {
        this.next = e;
    }

    public Element<T> getNext() {
        return next;
    }

    public void setPrev(Element<T> e) {
        this.prev = e;
    }

    public Element<T> getPrev() {
        return prev;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        /*if(value != null) {
            System.out.println("the value is null!");
            return false;
        }*/
        return value;
    }
}

That one return false; of getValue() is harmful. It prevents you from using generic types there. Also, if the content of Element for null, that the method getValue() return this null.

The class EmptyListException:

public class EmptyListException extends Exception {
    public EmptyListException(String message) {
        super(message);
    }
}

Now the class Deque:

import java.util.ArrayList;
import java.util.List;

public class Deque<T> {
    private Element<T> head;
    private Element<T> tail; 

    public Deque() {
        head = tail = null;
    }

    public boolean isEmpty() {
        return head == null && tail == null;
    }

    public boolean isFull() {
        return false;
    }

    public List<T> toList() {
        List<T> list = new ArrayList<>();     
        Element<T> elementFirst = getFirst();          
        while (elementFirst != null) {
            T value = elementFirst.getValue(); 
            list.add(value);
            elementFirst = elementFirst.getNext();
        }
        return list;     
    }

    public void addFirst(T o) {
        System.out.println("Addited in first: " + o);
        Element<T> e = new Element<>(o);
        e.setNext(head); 

        if (head != null) {
            head.setPrev(e);  
        }
        head = e; 
        if (tail == null) {
            tail = e; 
        }
    }

    public void addLast(T o) {
        System.out.println("Addited in last: " + o);
        Element<T> e = new Element<>(o);
        e.setPrev(tail); 

        if (tail != null) {
            tail.setNext(e);
        }
        tail = e;
        if (head == null) {
            head = e;
        }
    }

    public T removeFirst() throws EmptyListException {
        if (this.isEmpty()) {
            throw new EmptyListException("The deque is empty!");
        }
        T o = head.getValue();
        head = head.getNext(); 
        if (head == null) { 
            tail = null;
        } else {
            head.setPrev(null); 
        }
        System.out.println("Removed in first: " + o);
        System.out.println("New head: " + head.getValue());
        return o;
    }

    public T removeLast() throws EmptyListException {
        if (this.isEmpty()) {
            throw new EmptyListException("The deque is empty!");
        }
        T o = tail.getValue();
        tail = tail.getPrev(); 
        if (tail == null) {
            head = null;
        } else {
            tail.setNext(null); 
        }
        System.out.println("Removed in last: " + o);
        System.out.println("New tail: " + tail.getValue());
        return o;
    }

    public void clear() {
        System.out.println("");
        System.out.println("Start clear... ");
        while (!this.isEmpty()) {
            this.removeFirst();
        }
        System.out.println("End clear");
        System.out.println("");
    }

    public void listing() {
        Element<T> e = head;
        System.out.println("");
        System.out.println("Start listing... ");
        while (e != null) {
            System.out.println("Value: " + e.getValue());
            e = e.getNext();
        }
        System.out.println("End listing");
        System.out.println("");
    }
}

Here are several things to notice:

  1. In the method List() (whom he renames to toList()) you wore ArrayList<Integer>. That’s why I assumed the guy on the deck was Integer instead of T.

  2. Use throws Exception is a bad programming practice, as you should declare the release of specific exceptions instead of generic exceptions. Similarly, throw new Exception(...) is also bad practice for the same reason. When using EmptyListException, the specific exception is declared and released, instead of the generic exception.

  3. And third who call the method toList() on an empty deck is no problem, the result will be just an empty list.

  4. Do not declare type variables ArrayList. Although you can use the ArrayList, it is best to declare the type of the variable with List only. The reason for this is that there is a programming principle that tells you to code for an interface (List) and not for an implementation (ArrayList). By doing this, you can interoperate with other types of lists (such as LinkedList or lists returned by methods such as Arrays.asList(...), Collections.unmodifiableList(...) or List.of(...)).

Finally, let’s see yours Main:

import java.util.List;

public class Main {
    public static void main(String[] args) { 
        Deque<String> deque = new Deque<>();

        while (true) {         
            System.out.println("Select the option\n 1->Add first:\n 2->Add last:\n3->List:\n 4->Remove first:\n 5->Remove last:\n 6->Clear:\n 7->Exit:\n");
            String option = scanner.next();
            if ("1".equals(option)) {
                System.out.println("Enter with the value: ");
                value = scanner.next();
                deque.addFirst(value);
            } else if ("2".equals(option)) {
                System.out.println("Enter with the value: ");
                value = scanner.next();
                deque.addLast(value);
            } else if ("3".equals(option)) {
                List<String> myList = deque.toList();
                System.out.println(myList);
                deque.listing();
            } else if ("4".equals(option)) {
                try {
                    deque.removeFirst();
                } catch (EmptyListException e) {
                    System.out.println(e.getMessage());
                }
            } else if ("5".equals(option)) {
                try {
                    deque.removeLast();
                } catch (EmptyListException e) {
                    System.out.println(e.getMessage());
                }
            } else if ("6".equals(option)) {
                deque.clear();
            } else if ("7".equals(option)) {
                break;
            } else {
                System.out.println("Invalid option");
            }
        }
    }
}

The comments here are as follows::

  1. You had not declared the variable option adequately. The method next() of Scanner returns Strings, soon option be the type String. This may seem a little strange considering that the expected values are 1, 2, 3, 4, 5, 6 and 7. However, this allows if you want to switch to A, B, C, D, E, F and G not to have problems. Even something like Red, Orange, Yellow, Green, Blue, Indigo and Violet would work.

  2. I switched the do-while for while to prevent the Invalid option appears when the user wants to quit.

  3. I don’t know if the method listing() of your Deque is a good idea, since a System.out.println(deque.toList()) would be enough.

Ah, finally, there is no lambda or callback (yet) in your code.

  • 1

    Between us, with so many native exception classes, do you find it interesting to create your own exception class just to have a class with a proper name, with only 4 lines? I usually do that but I wonder how necessary it is.

  • 1

    @I thought I’d use IllegalStateException or NoSuchElementException, but both are RuntimeException and how to give catch in the menu for the program not to burst and in this specific case (it is rare, but this is one of them) it is good that the compiler force this, so I thought it better to be a Exception instead of RuntimeException.

  • 1

    The Deck of java.util uses NoSuchElementException in some methods. But it is a slightly different case as it offers "safe" and "unsafe" methods to access the elements such as getFirst and peekFirst.

  • 1

    @Victorstafusa thank you so much for the tips, this will really help me in future developments.

  • Now I would need to know the best way to implement something of lambda and callback in the code, for me really lambda is not totally clear to think of a good implementation.

  • @Sabrinab. Lambda is a way to implement a callback. However the approach that should be done is to find yourself a problem, seek the best tool to solve it. Trying to put lambda there would be the opposite approach, from a tool, trying to find a problem that it solves, which doesn’t work very well. However, a situation (among several possible ones) that naturally induces the need for Amblas/Callbacks would be to have listeners of events, so you can look for situations like this to learn more about Amblas and Callbacks.

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.