I need to implement custom linked list using generics.
Here is what I've done
public class Node {
Node next;
Object data;
public Node(Object data) {
next = null;
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object dataValue) {
data = dataValue;
}
public Node getNext() {
return next;
}
public void setNext(Node nextValue) {
next = nextValue;
}
}
public class LinkedList {
private Node head;
private int size;
public LinkedList() {
head = new Node(null);
size = 0;
}
public void add(Object data) {
Node node = new Node(data);
Node current = head;
while (current.getNext() != null) {
current = current.getNext();
}
current.setNext(node);
size++;
}
public int getSize() {
return size;
}
public String toString() {
Node current = head.getNext();
String elements = "";
while (current != null) {
elements += "[" + current.getData().toString() + "]";
current = current.getNext();
}
return elements;
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Hello there!");
LinkedList list = new LinkedList();
list.add("First node");
list.add("Second node");
list.add("Third node");
list.add("Fourth node");
list.add("Fifth node");
System.out.println("Linked list contains " + list.getSize() + " nodes");
System.out.println("Here they are: " + list);
}
}
I have no idea or just didn't quite understand where i should use generics and how? Any ideas?
You start with the Node class; specifically, you make it such that it can contain any type of data.
You do that in this manner:
Introduce a generic type parameter at the class level
public class Node<T> { }
Wherever you have Object, replace it with T.
T data;
Be sure to update references to other Node instances inside so that they're using the same generic argument.
Node<T> next;
Now, you can address the issues in your LinkedList class in a similar way.
Introduce a generic type parameter at the class level
public class LinkedList<T> { }
Change the argument of add from Object to T.
public void add(T data) { }
Add the generics to your Node instance(s) so that you're not using a raw type.
private Node<T> head;
You should consider going through the Generics tutorial. Specifically, read through the 'Generic Types' section.
Basically, your LinkedList and Node implementations need to be generic by simply declaring them as LinkedList<T> and Node<T>. Once you've changed the classes to be generic, you can then instantiate a parameterized LinkedList, such as:
LinkedList<String> stringList = new LinkedList<>();
The LinkedList is now type-safe and will only allow Strings to be stored.
Related
My goal is to create a tree-like object structure.
For this i created a class named Node (I removed the implementation because the problem still persists without it):
public class Node<S> {
public Node<S> addChild(Node<S> node) {
return this;
}
}
Important to know is that i want to define the generic type S only in the root node, all child nodes should automatically inherit from the root node.
Something like this:
new Node<String>().addChild(
new Node<>().addChild(
new Node<>()
)
)
I restricted the addChild method to only accept Nodes with the same generic type S,
so as far as i know my child node should know that it's generic type S has to be (in this example) String. However it seems like the generic type S gets lost after instantiating a new Node, because it gives me the following Exception:
error: incompatible types: Node<Object> cannot be converted to Node<String>
The use of <> requires type inference, and the argument of the first
addChild must be a Node, and just passing new Node<>() would do - infering from the return type.
But chaining to .addChild(new Node<>()) cannot infer anything, can only provide Node<Object>. So: one cannot use <>.
The problem is (of course) that you want addChild to return the head of the list, and keep adding to the tail of the list.
Normal practice is not to create Node instances, but just use the S values.
public class Node<S> {
private S value;
private Node<S> next;
public Node(S value) {
this.value = value;
}
public static <T> void print(Node<T> root) {
if (root == null) {
System.out.println("empty");
return;
}
System.out.print(root.value);
System.out.print(" --> ");
print(root.next);
}
public static <T> Node<T> addAll(T... values) {
Node<T> root = null;
Node<T> previous = null;
for (T value : values) {
Node<T> current = new Node<>(value);
if (root == null) {
root = current;
} else {
previous.next = current;
}
previous = current;
}
return root;
}
public static void main(String[] args) {
Node<String> root = Node.addAll("a", "b", "c", "d");
print(root);
}
}
Comparable to Collections.addAll or List.of. If you keep a Node<S> last field, you could indeed create something like:
public void addLast(S value) {
last.next = new Node<>(value);
}
This also shows a serious problem of the class: an empty list is not a Node.
One could use Optional<Node<S>> or a special constant for an empty list EMPTY - without value.
The normal solution is to have a container:
public class List<S> {
private class Node {
...
}
private Node<S> root;
private Node<S> last;
private int size;
public List<S> addLast(S value) {
Node<S> current = new Node<>(value);
if (root == null) {
root = current;
last = current;
} else {
last.next = current;
}
last = current;
++size;
return this;
}
private int size() {
return size;
}
...
}
Now everything fits.
List<String> nodes = new List<>()
.addLast("a")
.addLast("b")
.addLast("c")
.addLast("d");
After feedback, when wanting Node references.
Then discard chaining, and make Node public again.
public Node<S> addLast() {
addLast(null);
}
public Node<S> addLast(S value) {
Node<S> current = new Node<>(value);
if (root == null) {
root = current;
last = current;
} else {
last.next = current;
}
last = current;
++size;
return last;
}
List<String> nodes = new List<>()
Node<String> a = nodes.addLast();
Node<String> b = nodes.addLast();
var c = nodes.addLast();
var d = nodes.addLast();
One could use var for shortness.
What you are trying to do is something like this
public class Node<T> {
private Node<T> child;
private T data = null;
public Node (T data) {
this.data = data;
}
public T getData() {
return data;
}
public Node<T> getChild() {
return child;
}
public void addChild(Node<T> child) {
this.child = child;
}
#Override
public String toString() {
return "this node's data: " + data + "; has child? " + (child != null);
}
public static void main(String[] args) {
Node<String> root = new Node<> ("parent");
Node<String> child = new Node<>("child");
root.addChild(child);
System.out.println(root);
System.out.println(child);
}
}
If you were to execute this, it will output
this node's data: parent; has child? true
this node's data: child; has child? false
this node's data: 0; has child? false
this node's data: 1; has child? false
Notice how I can create nodes of type String and Integer. However, this class is incomplete if you want to create a tree structure. The implementation of "tree" will depend on what kind of tree you are talking about. For example, a simple binary tree will have two children at most. Other types of trees could have more children. Also, adding nodes to a tree might require balancing the tree.
Now, to your question, this answer suffices. I was able to demonstrate the use of generics to create Node objects of type T.
Is there a way to use the compareTo function when comparing objects, I'm not sure if it's just for Strings. I am trying add an node into its correct position in ascending order.
heres where I declare my attributes/constructor
private Node<E> head; //refers to the head of the node
private int size; // keeps track of the size of the list
// default constructor which creates empty ordered list
public OrderedList(){head = null; size = 0;}
Heres my insert function
public void insert(Object o)
{
Node n = new Node(o, null); // creates new node
// Node for first element greater than or equal
Node current = head.getLink();
Node before = head; // Node for right before the next one is found
// checks to see if list is empty
if(size == 0)
{
head = n;
}
// checks if element is smaller than the head
else if (o.compareTo(head.o) < 0)
{
n.getLink() = head;
head = n;
}
}
here is my node class
package project.pkg3;
public class Node<T>
{
private Object data;
private Node link;
public Node(Object o, Node l){data = o; link = l;}
public void setData(Object o){data = o;}
public void setLink(Node l){link = l;}
public Object getData(){return data;}
public Node getLink(){return link;}
}
I'm getting an error message when trying to check whether the element belongs in the front on this line
else if (o.compareTo(head.o) < 0)
telling me that it cannot find the symbol, which I'm not sure what that means
Im also getting another error message on this line
n.getLink() = head;
this one is telling me that it's an unexpected type
If your linked list must be sorted using compareTo(), then you need to make sure that the underlying data is comparable.
public class Node<T extends Comparable>
{
private T data;
private Node<T> link;
public Node(T o, Node<T> l) { data = o; link = l; }
public void setData(T o) { data = o; }
public void setLink(Node<T> l) {link = l; }
public T getData() { return data; }
public Node<T> getLink() { return link; }
}
Then this block
else if (o.compareTo(head.o) < 0)
{
n.getLink() = head;
head = n;
}
should be changed into this:
else if (
(o.getData() != null) ?
(o.getData().compareTo(head.getData()) < 0) :
(head.getData().compareTo(o.getData()) > 0)
)
{
n.setLink(head);
head = n;
}
I didn't look at your linked list implementation though, so I have no idea the other stuff are correct.
Your node class should implement java.lang.Comparable interface and override its compareTo() method as per your logic.
public class Node<T extends Comparable<T>>{
}
Your argument object would implement Comparable interface. For eg:
public class Name implements Comparable<Name> {
private String str1;
public int compareTo(Name o) {
//your logic here to compare object with itself
return this.str1.compareTo(o.str1);
}
}
I already wrote a small program of single linked list with add and traverse method in that. Now I want to convert it into a doubly linked list. I know all the concept of doubly linked list but I am facing little difficulty to implement it in my program.
public class SingleLinkList<T> {
private Node<T> head;
private Node<T> tail;
public void add(T element)
{
Node<T> nd = new Node<T>();
nd.setValue(element);
if (head==null)
{
head = nd;
tail = nd;
}
else
{
tail.setNextRef(nd);
tail = nd;
}
}
public void traverse(){
Node<T> tmp = head;
while(true){
if(tmp == null){
break;
}
System.out.println(tmp.getValue());
tmp = tmp.getNextRef();
}
}
public static void main (String args[])
{
SingleLinkList<Integer> s1 = new SingleLinkList<Integer>();
s1.add(2);
s1.add(3);
s1.add(3);
s1.traverse();
}
}
class Node<T> {
private T value;
private Node<T> nextRef;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public Node<T> getNextRef() {
return nextRef;
}
public void setNextRef(Node<T> nextRef) {
this.nextRef = nextRef;
}
public int compareTo(T arg)
{
if (arg==this.value)
{
return 0;}
else
{return 1;}
}
}
Add a Node<T> prevRef field to your list class with appropriate getters and setters and then add this method:
public void linkReverse(Node<T> head) {
if (head == null) {
return;
}
head.setPrevRef(null);
if (head.getNextRef() == null) {
return;
}
Node<T> prev = head;
Node<T> curr = head.getNextRef();
while (curr != null) {
curr.setPrevRef(prev);
prev = curr;
curr = curr.getNextRef();
}
}
This method will walk down a currently singly linked list and will link each node in reverse, leaving the list doubly linked.
Of course, you would need to modify the other methods as well, but this is at least a good starting point.
just add private Node<T> prevRef; instance variable to Node class, and set it during add() method. I suggest that traverse() will receive a boolean (or even better, enum) direction argument
My Node class:
public class Node<T>
{
protected T data;
protected Node<T> next;
protected Node<T> previous;
public Node()
{
this.data = null;
this.next = null;
this.previous = null;
}
public Node(T data)
{
this.data = data;
this.next = null;
this.previous = null;
}
public Node(T data, Node<T> next, Node<T> previous)
{
this.data = data;
this.next = next;
this.previous = previous;
}
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
}
public Node<T> getNext()
{
return next;
}
public void setNext(Node<T> next)
{
this.next = next;
}
public Node<T> getPrevious()
{
return previous;
}
public void setPrevious(Node<T> previous)
{
this.previous = previous;
}
}
My LinkedList class:
public class LinkedList<T extends Node<T>>
{
private Node<T> head;
private Node<T> tail;
private Node<T> currNode;
public LinkedList()
{
head = null;
tail = null;
currNode = null;
}
public LinkedList(Node<T> head)
{
this.head = head;
tail = head;
currNode = head;
}
public void resetHead()
{
currNode = head;
}
public void add(T data)
{
Node<T> newNode = new Node<T>(data);
newNode.next = null;
if(head == null)
{
head = newNode;
}
else
{
tail.next = newNode;
newNode.previous = tail;
tail = newNode;
}
}
public void addHead(T data)
{
Node<T> newNode = new Node<T>(data);
newNode.next = head;
head.previous = newNode;
head = newNode;
}
public void addAfter(T data, Node<T> previousNode)
{
Node<T> newNode = new Node<T>(data);
newNode.next = previousNode.next;
previousNode.next = newNode;
}
public void addBefore(T data, Node<T> nextNode)
{
Node<T> newNode = new Node<T>(data);
newNode.next = nextNode;
nextNode.previous = newNode;
}
public void delete(Node<T> nodeToDelete)
{
(nodeToDelete.getNext()).setPrevious(nodeToDelete.getPrevious());
(nodeToDelete.getPrevious()).setNext(nodeToDelete.getNext());
nodeToDelete.setNext(null);
nodeToDelete.setPrevious(null);
}
public boolean hasNext()
{
if(head == null)
{
return false;
}
else if(currNode.next != null)
{
currNode = currNode.getNext();
return true;
}
else
{
return false;
}
}
public boolean hasPrevious()
{
if(tail == null)
{
return false;
}
else if(currNode.previous != null)
{
currNode = currNode.getPrevious();
return true;
}
else
{
return false;
}
}
public Node<T> getHead()
{
return head;
}
public void setHead(Node<T> head)
{
this.head = head;
}
public Node<T> getTail()
{
return tail;
}
public void setTail(Node<T> tail)
{
this.tail = tail;
}
public Node<T> getCurrNode()
{
return currNode;
}
public void setCurrNode(Node<T> currNode)
{
this.currNode = currNode;
}
}
The error crops up when attempting to use any of the add/insert methods in LinkedList. For example, if I try to use the add(T data) method, like so: listOfChars.add('B');, I get the following error: The method add(Node) in the type LinkedList is not applicable for the arguments (char). What I expect it to do is to accept the data (in this case, the char 'B'), create a new node with 'B' as the data, and then put it in the linked list after the last node in the list. From my understanding, the method is expecting a Node instead of any generic data type, such as a char.
After doing some researching, I think somewhere in my TestLinkedList class, I have declared the LinkedList object incorrectly:
public class TestLinkedList
{
public static void main(String[]args)
{
Node<Character> n1 = new Node<Character>('A');
LinkedList listOfChars = new LinkedList(n1);
listOfChars.add('B');
}
}
but I can't figure out how to declare it correctly. I've tried LinkedList<Character>, LinkedList<Node>, LinkedList<Node<T>>, and LinkedList<Node<Character>>, but none of them are correct. Any help would be appreciated as this is my first time using generics and I am just trying to learn how to apply it to a Linked List I've made.
You need to fix two things. First, the class declaration of LinkedList says:
public class LinkedList<T extends Node<T>> {
which means that T has to be both a Node and the element of a Node. This doesn't work with Character since a Character is not an instance of Node. If you remove the constraint so that T can be any value, it works with Character.
public class LinkedList<T> {
Next you should add the generic part to the main file for the listOfChars as well:
LinkedList<Character> listOfChars = new LinkedList<Character>(n1);
Also make sure that you have imported the right LinkedList and not the java standard class.
You simply have to rewrite LinkedList from
LinkedList<T extends Node<T>>
to
LinkedList<T>
since in the code of LinkedList you already state that you are using Node objects.
You can use it like this:
public static void main(String[]args)
{
Node<Character> n1 = new Node<>('A');
LinkedList<Character> listOfChars = new LinkedList<>(n1);
listOfChars.add('B');
}
I think its a combination of both Todd's and Adams answer
public class LinkedList<T extends Node<T>>
to
public class LinkedList<T>
and
LinkedList<Character> listOfChars = new LinkedList<>(n1);
As you can infer from the error The method add(Node) in the type LinkedList is not applicable for the arguments (char)
This is a type error, your add method expects a Node but in the main you call add with type character.
Your LinkedList class is expecting an object of type T extends Node. Which would imply a custom class that extends your Node object, however that is not what you are looking to use to add objects to the linkedlist.
Ok, so your class declaration
public class LinkedList<T extends Node<T>>
is saying 'This is a class LinkedList parameterised by T where T is a subtype of Node<T>
This means if you wanted T = Character for example, you would need Character to be a subtype of Node<Character>, which doesn't really make sense.
You probably want to just parameterise your class by T eg public class LinkedList<T>
Then you can go LinkedList<Character> listOfChars = new LinkedList<Character>(n1)
or more succinctly LinkedList<Character> listOfChars = new LinkedList<>(n1) as the second generic parameter can be inferred
Create your LinkedList like this:
LinkedList<Character> listOfChars = new LinkedList<>(n1);
EDIT:
M4ver1k is right that this alone won't fix it. I missed the part that Adam Arold pointed out. Adam's answer fixes the problem and should be accepted. My answer just prevents the LinkedList from being implicitly defined as a LinkedList<Object>.
.
I have created this Node class for a singly linked list:
class Node{
int item;
Node next;
Node(int v){
item = v;
next = null;
}
}

I want to make a search for the node with the highest key in a method called findmax.But i want to check if the list is empty, and if so , to return null, otherwise return the node with the highest key. This is what i have done:
Node findmax(Node h){
if(h==null)
return null;
else{
//search
}
All i want to know is if the check i make to see if the list is Empty is correct.
Yes, the check you've done is correct if:
Node n = null;// init empty list
and:
n = new Node(3);// first item
However, I suggest you to create a list struct which is independent from the item it concatenates. That's what I mean:
The Node class:
public class Node
{
int value;
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
The list struct:
public interface IList
{
public int getNodeNumbers();
}
public class EmptyList implements IList
{
#Override public int getNodeNumbers() {
return 0;
}
}
public class ConsList implements IList
{
private Node node;
private IList next;
public ConsList(Node node, IList next) {
this.node = node;
this.next = next;
}
#Override public int getNodeNumbers() {
return 1 + next.getNodeNumbers();
}
}
How to use it:
public class Main
{
public static void main(String[] args) {
IList list1 = new ConsList(new Node(1),
new ConsList(new Node(2),
new ConsList(new Node(3),
new ConsList(new Node(4),
new EmptyList()))));
IList list2 = new EmptyList();
System.out.println(list1.getNodeNumbers() + " - " + list2.getNodeNumbers());
}
}
And now, a list is empty (you can create your own method isEmpty() into the IList interface) when getNodeNumbers() returns 0.