Search code examples
javalinked-list

Efficient way to store different data types


This is more of a conceptual question about how to most efficiently approach this issue, I'm utilizing LinkedLists created in a form I recently learned in class. I'll paste in both the node class I created as well as the LinkedList class itself. As I have the code set up now, I can only create lists with a single data type, in this instance being BufferedImages, but I want to be able to create one LinkedList for a BufferedImage and another for an int. Is there a better way to do this than making separate Node and LinkedList classes for each data type or overloading each class?

I want to be able to efficiently have two LinkedList objects storing different data types

//Node class:
import java.awt.image.BufferedImage;

public class Node {
    private BufferedImage data;
    private Node link;

    public Node getLink() {
        return link;
    }

    public void setLink(Node link){
        this.link = link;
    }

    public BufferedImage getData(){
        return data;
    }

    public void setData(BufferedImage data) {
        this.data = data;
    }

}

//LinkedList class:
import java.awt.image.BufferedImage;

public class LinkedList {

    private Node root;
    public Node getRoot(){
        return root;
    }

    public void add(BufferedImage data) {
        if (root == null) {
            root = new Node();
            root.setData(data);
        } else {
            Node tmp = root;
            while (tmp.getLink() != null) {
                tmp = tmp.getLink();
            }
            Node n = new Node();
            n.setData(data);
            tmp.setLink(n);
        }
    }

    public int size() {
        if (root == null) {
            return 0;
        }
        int count = 0;
        Node tmp = root;
        while (tmp != null) {
            count++;
            tmp = tmp.getLink();
        }
        return count;
    }

    public BufferedImage get(int pos) {
        if (pos < 0 || pos >= size()) {
            throw new IndexOutOfBoundsException("The index " + pos + " is out of bounds.");
        }

        Node tmp = root;
        for (int i = 0; i < pos; i++) {
            tmp = tmp.getLink();
        }
        return tmp.getData();
    }

    public void set(int pos, BufferedImage data) {
        if (pos < 0 || pos >= size()) {
            throw new IndexOutOfBoundsException("The index " + pos + " is out of bounds.");
        }
        Node tmp = root;
        for (int i = 0; i < pos; i++) {
            tmp = tmp.getLink();
        }
        tmp.setData(data);
    }

    //DELETES LAST DATA
    public void delete(){
        if(root != null){
            Node tmp = root;
            for(int i=0; i < size()-2; i++){
                tmp = tmp.getLink();
            }
            tmp.setLink(null);
        }
    }

}

Solution

  • As I find the baeldung article nod sufficient enough for beginners, I quickly re-wrote your code to support generics.

    Step 1: Make the inner workings generic, in this case your Node class:

    In simple terms, instead of giving Node a fixed content like BufferedImage, we introduce our own type parameter. Usually that is simply called <T>, but here I go with <MyGenericType> to show that it can be handled like any other type.

    The little downside is that we have to explicitly type Node<MyGenericType> wherever we use it, so this makes the code a bit longer.

    package stackoverflow.linkedlist;
    
    public class Node<MyGenericType> {
        private MyGenericType       data;
        private Node<MyGenericType> link;
    
        public Node<MyGenericType> getLink() {
            return link;
        }
    
        public void setLink(final Node<MyGenericType> link) {
            this.link = link;
        }
    
        public MyGenericType getData() {
            return data;
        }
    
        public void setData(final MyGenericType data) {
            this.data = data;
        }
    
    }
    

    Step 2: Make the main class generic, your LinkedList:

    Same here, introduce generics

    package stackoverflow.linkedlist;
    
    public class LinkedList<MyGenericType> {
    
        private Node<MyGenericType> root;
        public Node<MyGenericType> getRoot() {
            return root;
        }
    
        public void add(final MyGenericType data) {
            if (root == null) {
                root = new Node<>();
                root.setData(data);
            } else {
                Node<MyGenericType> tmp = root;
                while (tmp.getLink() != null) {
                    tmp = tmp.getLink();
                }
                final Node<MyGenericType> n = new Node<>();
                n.setData(data);
                tmp.setLink(n);
            }
        }
    
        public int size() {
            if (root == null) {
                return 0;
            }
            int count = 0;
            Node<MyGenericType> tmp = root;
            while (tmp != null) {
                count++;
                tmp = tmp.getLink();
            }
            return count;
        }
    
        public MyGenericType get(final int pos) {
            if (pos < 0 || pos >= size()) {
                throw new IndexOutOfBoundsException("The index " + pos + " is out of bounds.");
            }
    
            Node<MyGenericType> tmp = root;
            for (int i = 0; i < pos; i++) {
                tmp = tmp.getLink();
            }
            return tmp.getData();
        }
    
        public void set(final int pos, final MyGenericType data) {
            if (pos < 0 || pos >= size()) {
                throw new IndexOutOfBoundsException("The index " + pos + " is out of bounds.");
            }
            Node<MyGenericType> tmp = root;
            for (int i = 0; i < pos; i++) {
                tmp = tmp.getLink();
            }
            tmp.setData(data);
        }
    
        //DELETES LAST DATA
        public void delete() {
            if (root != null) {
                Node<MyGenericType> tmp = root;
                for (int i = 0; i < size() - 2; i++) {
                    tmp = tmp.getLink();
                }
                tmp.setLink(null);
            }
        }
    
    }
    

    Step 3: Showcase some examples

    Here I show you how to use the list. As you can see, we now have to define/introduce the 'concept' (aka Type) of BufferedImage from the outside, when we tell our list what type it should handle: LinkedList<BufferedImage> imageList = new LinkedList<>();

    package stackoverflow.linkedlist;
    
    import java.awt.image.BufferedImage;
    
    
    public class Usage {
    
    
    
        public static void main(final String[] args) {
            final LinkedList<BufferedImage> imageList = new LinkedList<>();
            final BufferedImage bi1 = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB);
            final BufferedImage bi2 = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB);
            imageList.add(bi1);
            imageList.add(bi2);
            System.out.println("Size of image list is now: " + imageList.size());
    
    
    
            final LinkedList<Integer> intList = new LinkedList<>();
            intList.add(Integer.valueOf(667));
            intList.add(Integer.valueOf(668));
            intList.add(Integer.valueOf(669));
            System.out.println("Size of Integer is now: " + intList.size());
    
    
    
            final LinkedList<DemoCombo> mixedList = new LinkedList<>();
            mixedList.add(new DemoCombo(bi1, 667));
            mixedList.add(new DemoCombo(bi2, 668));
            System.out.println("Size of mixed list is now: " + mixedList.size());
        }
    
    
    
        static private class DemoCombo {
            public final BufferedImage  image;
            public final int            value;
            public DemoCombo(final BufferedImage pImage, final int pValue) {
                image = pImage;
                value = pValue;
            }
        }
    
    
    
    }
    

    I also included the DemoCombo class, because I was unsure why you wanted to have a list of BufferedImage and one of int. Normally, if you have data that is related, like BufferedImage has a direct relation to the int, you should not have 2 different lists to store them in. In this case, it's a lot better to write a simple wrapper class, DemoCombo, so all data that belongs to each other is always together, and you do not have the pitfalls of having to manage 2 lists independently.