I'm trying to create a custom list, that ensures that only one type of reference can be added in my list, i'm trying to make it "type-safe".
The array is already set to an Object type, and everything inherits from Object so how do I substitute/subtype? For example if I want this list to ONLY be able to take String objects. Currently, my program can take both Integer type and String type as seen in the main method.
I can't use generics for this unfortunately.
MyArrayList
import java.util.ArrayList;
public class MyArrayList implements MyList {
private Object[] theList;
private Object type;
public MyArrayList() {
theList = new Object[0];
}
public MyArrayList(Object type) {
theList = new Object[0];
setType(type);
}
public Object getType() {
return type;
}
public void setType(Object type) {
if (type == Integer.class || type == String.class || type == Double.class) {
this.type = type;
} else {
System.out.println("Invalid value");
}
}
public boolean add(Object toAdd) {
if (toAdd != null && toAdd == type.getClass()) {
Object[] temp = new Object[theList.length + 1];
for (int index = 0; index < theList.length; index++) {
temp[index] = theList[index];
}
temp[theList.length] = toAdd;
theList = temp;
return true;
} else {
System.out.println("Invalid type");
return false;
}
}
public Object get(int index){
if(index >= 0 && index < theList.length) {
return theList[index];
} else {
return null;
}
}
public Object remove(int index) {
if (index >= 0 && index < theList.length) {
Object[] newList = new Object[theList.length - 1];
int j = 0;
for (int i = 0; i < theList.length; i++) {
if (i == index) {
continue;
}
newList[j++] = theList[i];
}
theList = newList;
return newList;
}
return null;
}
public int size(){
return theList.length;
}
public boolean isEmpty(){
if(theList.length > 0) {
return true;
} else {
return false;
}
}
public void display() {
for(Object thing: theList) {
System.out.print(thing + ", ");
}
System.out.println();
}
}
MyList
/**
* Write a description of interface List here.
*
* #author (your name)
* #version (a version number or a date)
*/
public interface MyList
{
/**
* Adds a new element at the end of the list.
* #param the object to add
* #return true if element successfully added, otherwise false
*/
boolean add(Object toAdd);
/**
* Gets the object at the specified index.
* #param index value of object to get
* #return object at that index
*/
Object get(int index);
/**
* Removes specified object from list.
* #param index value of object to remove
* #return the object removed
*/
Object remove(int index);
/**
* Returns size of the list
* #return number of elements in the list
*/
int size();
/**
* #return true if the list has no elements, false otherwise
*/
boolean isEmpty();
}
Main
import com.sun.jdi.IntegerType;
public class Main {
public static void main(String[] args) {
Object list = new Object();
Object myList = new MyArrayList(list);
MyArrayList newList = new MyArrayList(myList);
newList.add(2);
newList.add("Tom");
newList.add(0.0);
newList.display();
Without using generics, you can't achieve compile-time type safety for this. But you can cause an exception to be raised at runtime if you create the array dynamically with a component type that is not java.lang.Object, with
public class MyArrayList implements MyList {
private Object[] theList;
public MyArrayList(Class<?> type) {
this.theList = (Object[]) java.lang.reflect.Array.newInstance(type, 10);
}
...
This way, if the class passed to create the array is String.class, then adding an integer to it would result in a java.lang.ArrayStoreException being thrown, and you can try/catch this exception wherever this.theList[n] = object is.
It's worth mentioning, though, that the correct way to do this is to use generics both in your class and in caller classes.
You could create a self-made double linked list by creating a class that is an element in your list that holds the previous element and the following element of the list. Each of these elements could have one parameter, that is of the specific type you want to safe.
Well, without generics you'll have to do all type-checking yourself, and casting at the call site cannot be avoided.
You could, for example, accept a class Object of the desired type in the constructor, and check that all added Objects are instances of that type:
public class MyArrayList implements MyList {
private Object[] theList;
private final Class type;
// I've omitted the generic wildcard here, cause "no generics"
// Suppress wildcard warnings here if needed
// never do this in real, post-Java5 code
public MyArrayList(Class genericType) {
//check any preconditions you want on the class
this.type = genericType;
this.theList = new Object[0];
}
public void add(Object toAdd) {
// omitted code for growing the array here
this.temp[theList.length] = this.type.cast(toAdd); //throws Exception on type mismatch
}
}
This uses Class.cast which throws a ClassCastException on mismatch. You could also just use Class.isInstance to check the tyoe and decide yourself what to do (e.g. throw a different exception).
Note that your setType would be a bad idea, because once you've got that, you can no longer be sure that the values in the array are actually of that type. You'd have to clear the array when setting the type, which makes the method useless - at that point you can just create a new instance.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 3 years ago.
this is for an assignment. I need to implement a queue using linked lists. I keep getting a null pointer exception while using my enqueue method for my listQueue Class. I'm not too sure what is going on. Below are my main class and listQueue Class.
public class Driver{
public static void main(String[] args) {
MyQueue<Integer> q = null;
boolean useList = true; // make it false if you want to array implementation
if (useList)
q = new ListQueue<Integer>(); // default setting: use a list to implement the queue. You can change it.
else
q = new ArrayQueue<Integer>();
for(int i = 0; i < 1000; i++) // add a large number of items onto the queue
{
q.enqueue(i);
}
System.out.println("Now, dequeue items!");
while(!q.isEmpty())
{
System.out.print(q.dequeue() + " ");
}
System.out.println("\nEnd of dequeueing");
// you should fully test/debug all methods!
}
}
public class ListQueue<T> implements MyQueue<T> {
private class Node {
T item; // public
Node next; // null
}
private Node head = null;
private Node tail = null;
private int size = 0; //size of the list
public ListQueue(){}
/**
* is empty?
*/
public boolean isEmpty() {
// your implementation here:
if (size == 0) {
return true;
}//end if
return false;
}//end isEmpty
/**
* #return the number of items in the queue
*/
public int size() {
// your implementation here:
return size;
}//end size
/**
* enqueue: add an element at the tail of queue
*/
public void enqueue(T item) {
// your implementation here:
if (size == 0) {
head = new Node();
head.item = item;
head.next=tail;
size++;
}//end if
else{
Node newNode = new Node();
newNode.item=item;
tail.next = newNode;
tail = newNode;
size++;
}//end else
}
/**
* dequeue: remove and return the head of the queue
*
* #return the deleted value
* #throws NoSuchElementException if queue is empty
*/
public T dequeue() {
// your implementation here:
if(isEmpty()){
throw new NoSuchElementException();
}//end if
else{
Node temp = head;
head = head.next;
size--;
return temp.item;
}//end else
}//end dequeue
/**
* peek: view the head of queue without removing it.
*
* #return Null if queue is empty
*/
public T peek() {
// your implementation here:
if(isEmpty()){
return null;
}//end if
else{
return head.item;
}//end else
}//end peek
}//end ListQueue
After adding one element to the list, your tail pointer is null. When you add another element to the list, you try to dereference the null pointer which causes the error:
tail.next = newNode;
You need to make sure that for non-empty queues, the head and tail pointers are always pointing to a node. When the queue is size 1, the head and tail pointers should point to the same node.
I look for a queue that stores up to N elements for a certain time (i.e. 10 sec) or should dispose the oldest value if full.
I found a similar queue in the Apache Collections (CircularFifoQueue JavaDoc) that misses the aspect of time to live. A full fletched message broker seems like an overhead for my small project.
Do you mind giving me a hint how I should implement this? Shall I filter out old values while I poll for elements?
There is a class called LinkedHashMap which has a special method for removing stale data. From the documentation:
protected boolean removeEldestEntry(Map.Entry eldest)
Returns true if this map should remove its eldest entry.
The method removeEldestEntry is called whenever anything is added to the list (queue). If it returns true then the eldest entry is removed to make room for the new entry, otherwise nothing is removed. You can add your own implementation which checks the timestamp on the eldest entry and returns true if it be older than a threshhold for expiration (e.g. 10 seconds). So your implementation might look something like this:
protected boolean removeEldestEntry(Map.Entry eldest) {
long currTimeMillis = System.currentTimeMillis();
long entryTimeMillis = eldest.getValue().getTimeCreation();
return (currTimeMillis - entryTimeMillis) > (1000*10*60);
}
I think java.util.LinkedHashMap is the solution for you. It has a removeEldest() method which is called whenever an entry is put in the map. You can override it to indicate if the eldest entry should be removed.
The JavaDoc gives a good example:
private static final int MAX_ENTRIES = 100;
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
This removes the eldest entry if the map has more than 100 entries.
Pro-actively removing items after 10 seconds would require a separate thread to check age and remove old items. I am guessing this is not what you want, judging by your description.
I used to following queue implementation. The code is heavily based on Apaches CircularFifoQueue and is only weakly tested. Moreover the implementation is not thread-safe and not serializable.
Leave a comment if you spot a mistake.
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
/**
* TimedQueue is a first-in first-out queue with a fixed size that
* replaces its oldest element if full.
* <p>
* The removal order of a {#link TimedQueue} is based on the
* insertion order; elements are removed in the same order in which they
* were added. The iteration order is the same as the removal order.
* <p>
* The {#link #add(Object)}, {#link #remove()}, {#link #peek()}, {#link #poll},
* {#link #offer(Object)} operations all perform in constant time.
* All other operations perform in linear time or worse.
* <p>
* This queue prevents null objects from being added and it is not thread-safe and not serializable.
*
* The majority of this source code was copied from Apaches {#link CircularFifoQueue}: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/queue/CircularFifoQueue.html
*
* #version 1.0
*/
public class TimedQueue<E> extends AbstractCollection<E>
implements Queue<E> {
/** Underlying storage array. */
private Item<E>[] elements;
/** Array index of first (oldest) queue element. */
private int start = 0;
/**
* Index mod maxElements of the array position following the last queue
* element. Queue elements start at elements[start] and "wrap around"
* elements[maxElements-1], ending at elements[decrement(end)].
* For example, elements = {c,a,b}, start=1, end=1 corresponds to
* the queue [a,b,c].
*/
private transient int end = 0;
/** Flag to indicate if the queue is currently full. */
private transient boolean full = false;
/** Capacity of the queue. */
private final int maxElements;
private TimeUnit unit;
private int delay;
/**
* Constructor that creates a queue with the default size of 32.
*/
public TimedQueue() {
this(32);
}
/**
* Constructor that creates a queue with the specified size.
*
* #param size the size of the queue (cannot be changed)
* #throws IllegalArgumentException if the size is < 1
*/
public TimedQueue(final int size) {
this(size, 3, TimeUnit.SECONDS);
}
#SuppressWarnings("unchecked")
public TimedQueue(final int size, int delay, TimeUnit unit) {
if (size <= 0) {
throw new IllegalArgumentException("The size must be greater than 0");
}
elements = new Item[size];
maxElements = elements.length;
this.unit = unit;
this.delay = delay;
}
/**
* Constructor that creates a queue from the specified collection.
* The collection size also sets the queue size.
*
* #param coll the collection to copy into the queue, may not be null
* #throws NullPointerException if the collection is null
*/
public TimedQueue(final Collection<? extends E> coll) {
this(coll.size());
addAll(coll);
}
/**
* Returns the number of elements stored in the queue.
*
* #return this queue's size
*/
#Override
public int size() {
int size = 0;
for(int i = 0; i < elements.length; i++) {
if(validElement(i) != null) {
size++;
}
}
return size;
}
/**
* Returns true if this queue is empty; false otherwise.
*
* #return true if this queue is empty
*/
#Override
public boolean isEmpty() {
return size() == 0;
}
private boolean isAtFullCapacity() {
return size() == maxElements;
}
/**
* Clears this queue.
*/
#Override
public void clear() {
full = false;
start = 0;
end = 0;
Arrays.fill(elements, null);
}
/**
* Adds the given element to this queue. If the queue is full, the least recently added
* element is discarded so that a new element can be inserted.
*
* #param element the element to add
* #return true, always
* #throws NullPointerException if the given element is null
*/
#Override
public boolean add(final E element) {
if (null == element) {
throw new NullPointerException("Attempted to add null object to queue");
}
if (isAtFullCapacity()) {
remove();
}
elements[end++] = new Item<E>(element);
if (end >= maxElements) {
end = 0;
}
if (end == start) {
full = true;
}
return true;
}
/**
* Returns the element at the specified position in this queue.
*
* #param index the position of the element in the queue
* #return the element at position {#code index}
* #throws NoSuchElementException if the requested position is outside the range [0, size)
*/
public E get(final int index) {
final int sz = size();
if (sz == 0) {
throw new NoSuchElementException(
String.format("The specified index (%1$d) is outside the available range because the queue is empty.", Integer.valueOf(index)));
}
if (index < 0 || index >= sz) {
throw new NoSuchElementException(
String.format("The specified index (%1$d) is outside the available range [0, %2$d]",
Integer.valueOf(index), Integer.valueOf(sz-1)));
}
final int idx = (start + index) % maxElements;
return validElement(idx);
}
private E validElement(int idx) {
if(elements[idx] == null){
return null;
}
long diff = System.currentTimeMillis() - elements[idx].getCreationTime();
if(diff < unit.toMillis(delay)) {
return (E) elements[idx].getValue();
} else {
elements[idx] = null;
return null;
}
}
//-----------------------------------------------------------------------
/**
* Adds the given element to this queue. If the queue is full, the least recently added
* element is discarded so that a new element can be inserted.
*
* #param element the element to add
* #return true, always
* #throws NullPointerException if the given element is null
*/
public boolean offer(E element) {
return add(element);
}
public E poll() {
if (isEmpty()) {
return null;
}
return remove();
}
public E element() {
if (isEmpty()) {
throw new NoSuchElementException("queue is empty");
}
return peek();
}
public E peek() {
if (isEmpty()) {
return null;
}
return (E) elements[start].getValue();
}
public E remove() {
if (isEmpty()) {
throw new NoSuchElementException("queue is empty");
}
final E element = validElement(start);
if (null != element) {
elements[start++] = null;
if (start >= maxElements) {
start = 0;
}
full = false;
}
return element;
}
/**
* Increments the internal index.
*
* #param index the index to increment
* #return the updated index
*/
private int increment(int index) {
index++;
if (index >= maxElements) {
index = 0;
}
return index;
}
/**
* Decrements the internal index.
*
* #param index the index to decrement
* #return the updated index
*/
private int decrement(int index) {
index--;
if (index < 0) {
index = maxElements - 1;
}
return index;
}
/**
* Returns an iterator over this queue's elements.
*
* #return an iterator over this queue's elements
*/
#Override
public Iterator<E> iterator() {
return new Iterator<E>() {
private int index = start;
private int lastReturnedIndex = -1;
private boolean isFirst = full;
public boolean hasNext() {
return (isFirst || index != end) && size() > 0;
}
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
isFirst = false;
lastReturnedIndex = index;
index = increment(index);
if(validElement(lastReturnedIndex) == null) {
return next();
} else {
return validElement(lastReturnedIndex);
}
}
public void remove() {
if (lastReturnedIndex == -1) {
throw new IllegalStateException();
}
// First element can be removed quickly
if (lastReturnedIndex == start) {
TimedQueue.this.remove();
lastReturnedIndex = -1;
return;
}
int pos = lastReturnedIndex + 1;
if (start < lastReturnedIndex && pos < end) {
// shift in one part
System.arraycopy(elements, pos, elements, lastReturnedIndex, end - pos);
} else {
// Other elements require us to shift the subsequent elements
while (pos != end) {
if (pos >= maxElements) {
elements[pos - 1] = elements[0];
pos = 0;
} else {
elements[decrement(pos)] = elements[pos];
pos = increment(pos);
}
}
}
lastReturnedIndex = -1;
end = decrement(end);
elements[end] = null;
full = false;
index = decrement(index);
}
};
}
private static final class Item<E> {
private long creationTime;
private E in;
public Item(E in) {
this.in = in;
creationTime = System.currentTimeMillis();
}
public E getValue() {
return in;
}
public long getCreationTime() {
return creationTime;
}
}
}
I keep getting an ArrayStoreError when I use my PriorityQueItem.add(E item) method. The flow of my code is...
if (student.deductCoins(coins)) { //if the student has enough coins...
PriorityQueueItem<Student> newPQI = new PriorityQueueItem<Student>
(coins); //...create a new PriorityQueueItem<E> object
// using that amount for the priority
newPQI.add(student); //add the student to the queue associated with this
//PriorityQueueItem object
...in the Course class and then my PriorityQueItem.add(E item) is...
public void add(E item) {getList().enqueue(item);}
...which uses the Queue<E>.enqueue(E item) method to add the add to the queue of new PriorityQueueItem<E>. Each PriorityQueueItem has an integer value and a Queue<E> queue assigned to it on creation; getList() gets the queue.
Based on the stack trace, the error is triggered when the line queue[front]=item in Queue<E>.enqueue(E item) is reached. According to the API, the ArrayStoreException occurs when you try to add the wrong type of an item to an array but I cannot see where in this sequence where it happens. Any help will be appreciated.
#Erin and #Thomas:
/**
*
* Class to represent object stored at every entry in the PriorityQueue. ie, The
* internal node structure of {#link PriorityQueue}
*
* #author CS367
*
* #param
* the generic type of the data content stored in the list
*/
public class PriorityQueueItem implements Comparable> {
private int priority;
private Queue<E> queue;
public PriorityQueueItem(int priority) {
this.priority = priority;
this.queue = new Queue<E>();
}
public int getPriority() {
// TODO
return this.priority;
}
public Queue<E> getList() {
// TODO
return this.queue;
}
/**
* Add an item to the queue of this PriorityQueueItem object
* #param
*
* #param student
* item to add to the queue
*/
public void add(E item) {
getList().enqueue(item);
}
/**
* Compares this Node to another node on basis of priority
*
* #param o
* other node to compare to
* #return -1 if this node's priority is lesser, +1 if this nodes priority
* is higher after, else 0 if priorities are the same.
*/
#Override
public int compareTo(PriorityQueueItem<E> o) {
int x = 0;
if (this.getPriority() > o.getPriority())
x = 1;
if (this.getPriority() == o.getPriority())
x = 0;
if (this.getPriority() < o.getPriority())
x = 1;
return x;
}
}
/**
* An ordered collection of items, where items are added to the rear and removed
* from the front.
*/
public class Queue implements QueueADT {
private static final int MAX_CAPACITY = 100;
private E[] queue;
private int size;
private int front;
private int rear;
// TODO
// You may use a naive expandable circular array or a chain of listnodes.
// You may NOT use Java's predefined classes such as ArrayList or
// LinkedList.
#SuppressWarnings("unchecked")
public Queue() {
this.queue = (E[]) new Array [MAX_CAPACITY];
this.size = 0;
this.front = 0;
this.rear = 0;
}
/**
* Adds an item to the rear of the queue.
*
* #param item
* the item to add to the queue.
* #throws IllegalArgumentException
* if item is null.
*/
public void enqueue(E item) {
if (item == null) {
throw new IllegalArgumentException();
}
// if number of items exceeds size limits of the array
// then expand the array
if (!(size < queue.length - 1)) {
this.expandCapacity();
}
queue[rear] = item;
if (rear < queue.length - 1) {
rear++;
} else {
rear = 0;
}
size++;
}
/**
* Removes an item from the front of the Queue and returns it.
*
* #return the front item in the queue.
* #throws EmptyQueueException
* if the queue is empty.
*/
public E dequeue() {
if (isEmpty()) {
throw new EmptyQueueException();
}
E next = this.queue[front];
queue[front] = null;
if (this.front < queue.length - 1) {
this.front++;
} else {
this.front = 0;
}
size--;
return next;
}
/**
* Returns the item at front of the Queue without removing it.
*
* #return the front item in the queue.
* #throws EmptyQueueException
* if the queue is empty.
*/
public E peek() {
return this.queue[front];
}
/**
* Returns true iff the Queue is empty.
*
* #return true if queue is empty; otherwise false.
*/
public boolean isEmpty() {
return this.size == 0;
}
/**
* Removes all items in the queue leaving an empty queue.
*/
public void clear() {
while (!isEmpty()) {
this.dequeue();
}
}
/**
* Returns the number of items in the Queue.
*
* #return the size of the queue.
*/
public int size() {
return this.size;
}
#SuppressWarnings("unchecked")
private void expandCapacity() {
E[] newQueue;
newQueue = (E[]) Array.newInstance(PriorityQueueItem.class,
this.size() * 2);
for (int i = 0; i < queue.length; i++) {
newQueue[i] = this.queue[i];
}
this.queue = newQueue;
}
}
I have a LinkedList class that implements iterable interface, in the LinkedList class i have an inner node class. I have another TestLinkedList class that runs JUnit 4. The test class will check all of my functions in the linkedlist class.
Here is my LinkedListClass:
public class LinkedList<T> implements Iterable<T>
{
public class Node
{
private T value;
private Node next;
public Node(Node next, T value)
{
this.next = next;
this.value = value;
}
/**
* Returns the next node in the linked list.
*
* #return The next node in the linked list.
*/
public Node getNext()
{
return this.next;
}
/**
* Set the next node in the linked list.
*
* #param next
* The node to be added to the LinkedList.
*/
public void setNext(Node next)
{
this.next = next;
}
/**
* Return the value contained in the node.
*
* #return the value contained in the node.
*/
public T getValue()
{
return this.value;
}
/**
* Set the node with the value given.
*
* #param value
* The value to be placed in the node.
*/
public void setValue(T value)
{
this.value = value;
}
public String toString()
{
return "Node " + this.value;
}
}
public Node front;
public LinkedList()
{
front = null;
}
/**
* Return the number of elements in the LinkedList
*
* #return The size of the LinkedList
*/
public int getSize()
{
Node current = front;
int count = 0;
while (current != null)
{
count++;
current = current.getNext();
}
return count;
}
/**
* Return true if the LinkedList is empty, false otherwise
*
* #return true if the LinkedList is empty, false otherwise
*/
public boolean isEmpty()
{
return front == null;
}
/**
* Insert a node at the front of the linked list. The first variable should now point to this node. Wrap it in a
* node and add it to the list. Do not add the Node if it already exists in the list.
*
* #param node
* The node to be inserted into the linked list.
* #return true if inserted, false if already in list and cannot be inserted.
*/
public boolean insertFront(T element)
{
Node current = front;
boolean isExist = false;
while (current != null)
{
if (current.getValue().equals(element))
{
isExist = true;
}
current = current.getNext();
}
if (isExist == true)
{
return false;
}
else
{
front = new Node(front, element);
return true;
}
}
/**
* Insert a node at the back of the linked list. Wrap it in a node and add it to the list. Do not add the Node if it
* already exists in the list.
*
* #param node
* The node to be inserted into the linked list.
* #return true if inserted, false if already in list and cannot be inserted.
*/
public boolean insertBack(T element)
{
if (front == null)
{
insertFront(element);
return true;
}
else
{
Node current = front;
Node temp = current;
while (current!= null && !current.getValue().equals(element))
{
current = current.getNext();
}
if (current != null)
{
return false;
}
else
{
while(temp.getNext() != null)
{
temp = temp.getNext();
}
temp.setNext(new Node(null, element));
return true;
}
}
}
/**
* Insert the given node after the currentNode given. Wrap it in a node and add it in a position after the node
* specified by the variable {#code currentNode}. Throws a NodeNotFoundException if it can't found the node given.
* Do not add the Node if it already exists in the list.
*
* #param currentNode
* The node to look for to add the given node behind.
* #param node
* The element to be inserted into the linked list.
* #throws NodeNotFoundException
* Thrown if the element given is not found
* #return true if inserted, false if already in list and cannot be inserted.
*/
public boolean insertAfter(T currentElement, T element) throws NodeNotFoundException
{
Node current = front;
Node check = current;
while (current != null && !current.getValue().equals(currentElement))
{
current = current.getNext();
}
if (current == null)
{
throw new NodeNotFoundException("" + currentElement);
}
else
{
while(check != null && !check.getValue().equals(element))
{
check = check.getNext();
}
if (check != null)
{
return false;
}
else
{
current.setNext(new Node(current, element));
return true;
}
}
}
/**
* Insert the given node before the currentNode given. Wrap it in a node and add it in a position after the node
* specified by the variable {#code currentNode}. Throws a NodeNotFoundException if it can't found the node given.
* Do not add the Node if it already exists in the list.
*
* #param currentNode
* The node to look for to add the given node in front of.
* #param node
* The element to be inserted into the linked list.
*
* #throws NodeNotFoundException
* Thrown if the element given is not found
* #return true if inserted, false if already in list and cannot be inserted.
*/
public boolean insertBefore(T currentElement, T element) throws NodeNotFoundException
{
if (front == null)
{
throw new NodeNotFoundException("" + currentElement);
}
if (front.getValue().equals(currentElement))
{
insertFront(element);
return true;
}
Node previous = null;
Node current = front;
Node check = current;
while (current != null && !current.getValue().equals(currentElement))
{
previous = current;
current = current.getNext();
}
if (current == null)
{
throw new NodeNotFoundException("" + currentElement);
}
else
{
while (check != null && !check.getValue().equals(element))
{
check = check.getNext();
}
if (check != null)
{
return false;
}
previous.setNext(new Node(current, element));
return true;
}
}
/**
* Remove the node matches the given element. Return the element that is removed. Throws NodeNotFoundException if
* the element is not found.
*
* #param element
* The element to find and remove.
* #return Return the node that contains the element that was removed.
* #throws NodeNotFoundException
* Thrown if the element to be found can't be found.
*/
public T remove(T element) throws NodeNotFoundException
{
if(front == null)
{
throw new NodeNotFoundException(element.toString());
}
if( front.getValue().equals(element) )
{
front = front.getNext();
return element;
}
Node current = front;
Node previous = null;
while(current != null && !current.getValue().equals(element) )
{
previous = current;
current = current.getNext();
}
if(current == null)
{
throw new NodeNotFoundException(element.toString());
}
previous.setNext(current.getNext());
return element;
}
/**
* Remove all nodes in the LinkedList, return all nodes in an ArrayList.
*
*
* #return Returns all nodes in an ArrayList.
*/
public ArrayList<T> removeAll() throws NodeNotFoundException
{
Node current = front;
ArrayList<T> arrayList = new ArrayList<T>();
while (current != null)
{
arrayList.add(current.getValue());
current = current.getNext();
}
front = null;
return arrayList;
}
/**
* Return true if the element passed in is in the linked list.
*
* #param element
* The element to check for.
* #return true if the element exists in the linked list, false otherwise.
*/
public boolean contains(T element)
{
Node current = front;
while (current != null)
{
if (current.value.equals(element))
{
return true;
}
current = current.getNext();
}
return false;
}
/**
* Find an element and return it if it is found, otherwise return null
*
* #param element
* The element to look for.
* #return The element if found, null if not.
*/
public T findElement(T element)
{
Node check = front;
while (check != null && !check.getValue().equals(element))
{
check = check.getNext();
}
if (check == null)
{
return null;
}
else
{
return check.getValue();
}
}
/**
* Find an element and return it if it is found, otherwise return null
*
* #param element
* The element to look for.
* #return The element if found, null if not.
*/
public Node findNode(T element)
{
if(contains(element) == false)
{
return null;
}
else
{
Node check = front;
while (check != null && !check.getValue().equals(element))
{
check = check.getNext();
}
return check;
}
}
/**
* Converts the LinkedList to an ArrayList.
*
* #return An ArrayList containing all elements that are contained within the linked list.
*/
public ArrayList<T> convert()
{
Node current = front;
ArrayList<T> arrayList = new ArrayList<T>();
while (current != null)
{
arrayList.add(current.getValue());
current = current.getNext();
}
return arrayList;
}
/**
* Return the linked list as a string in the format element -> element -> element. For example
* "first -> second -> third"
*
* #return This linked list in the form of a string.
*/
#Override
public String toString()
{
Node current = front;
String s = "";
while (current.getNext() != null)
{
s += current.getValue() + "->";
current = current.getNext();
}
s += "" + current.getValue();
return s;
}
/*
* (non-Javadoc)
*
* #see java.lang.Iterable#iterator()
*/
#Override
public Iterator<T> iterator()
{
return new LinkedListIterator<T>(new LinkedList<T>());
}
}
This is my LinkedListIterator class:
public class LinkedListIterator<T> implements Iterator<T>
{
LinkedList<T>.Node previous;
LinkedList<T>.Node current;
public LinkedListIterator(LinkedList<T> list)
{
current = list.front;
}
/*
* (non-Javadoc)
*
* #see java.util.Iterator#hasNext()
*/
#Override
public boolean hasNext()
{
return current != null;
}
/*
* (non-Javadoc)
*
* #see java.util.Iterator#next()
*/
#Override
public T next()
{
if (!hasNext())
{
return null;
}
T temp = current.getValue();
previous = current;
current = current.getNext();
return temp;
}
/*
* (non-Javadoc)
*
* #see java.util.Iterator#remove()
*/
#Override
public void remove()
{
previous.setNext(current.getNext());
}
}
Here is my TestLinkedList class:
public class TestLinkedList
{
private static String FIRST = "First";
private static String SECOND = "Second";
private static String THIRD = "Third";
private static String FOURTH = "Fourth";
private static String MISSING = "Missing";
private static String TEST_STRING = "First->Second->Third->Fourth";
private static String TEST_ARRAY = "[First,Second,Third,Fourth]";
private LinkedList<String> testList;
#Before
public void setUp() throws NodeNotFoundException
{
testList = new LinkedList<String>();
}
#Test
public void testNextAndHasNext() throws NodeNotFoundException
{
insertAll(testList);
assertTrue("Next/HasNext failed", compareListToStrings(testList, FIRST, SECOND, THIRD, FOURTH));
}
#Test
public void testIsEmpty() throws NodeNotFoundException
{
insertAll(testList);
assertFalse("isEmpty Failed", testList.isEmpty());
removeViaIterator(testList);
assertTrue("isEmpty Failed after emptying", testList.isEmpty());
}
#Test
public void testIteratorRemove() throws NodeNotFoundException
{
insertAll(testList);
removeViaIterator(testList);
Iterator<String> iter = testList.iterator();
assertFalse("Iterator remove failed", iter.hasNext());
}
#Test
public void testInsertFrontAndBack()
{
assertTrue("insertFront failed on first insert", testList.insertFront(FIRST));
assertTrue("insertFront failed, list has too many elements", compareListToStrings(testList, FIRST));
assertFalse("insertFront failed, same element added to list", testList.insertFront(FIRST));
assertTrue("insertBack failed when inserting element not in list", testList.insertBack(FOURTH));
assertTrue("insertBack failed, list has wrong elements", compareListToStrings(testList, FIRST, FOURTH));
assertFalse("insertBack failed, same element already added to list", testList.insertBack(FOURTH));
}
#Test(expected = NodeNotFoundException.class)
public void testNodeNotFound() throws NodeNotFoundException
{
testList.insertBefore(MISSING, MISSING);
}
#Test
public void testInsertBeforeAndAfter() throws NodeNotFoundException
{
testList.insertFront(FOURTH);
testList.insertFront(FIRST);
assertTrue("insertBefore failed", testList.insertBefore(FOURTH, THIRD));
assertTrue("insertBefore failed, list does not have right elements",
compareListToStrings(testList, FIRST, THIRD, FOURTH));
assertFalse("insertBeforeFailed on inserting duplicate elements", testList.insertBefore(FOURTH, THIRD));
assertTrue("insertAfter failed", testList.insertAfter(FIRST, SECOND));
assertTrue("insertAfter failed, list does not have right elements",
compareListToStrings(testList, FIRST, SECOND, THIRD, FOURTH));
assertFalse("insertAfter failed on inserting duplicate elements", testList.insertAfter(FIRST, SECOND));
}
#Test
public void testToStringAndToArray()
{
testList.insertFront(FOURTH);
testList.insertFront(THIRD);
testList.insertFront(SECOND);
testList.insertFront(FIRST);
String listString = testList.toString();
assertTrue("toString failed", listString.replaceAll("\\s+", "").equals(TEST_STRING));
String arrayString = testList.convert().toString();
assertTrue("convert failed", arrayString.replaceAll("\\s+", "").equals(TEST_ARRAY));
}
#Test
public void testContains()
{
testList.insertFront(FOURTH);
testList.insertFront(THIRD);
testList.insertFront(SECOND);
testList.insertFront(FIRST);
assertTrue("Contains failed", testList.contains(FIRST));
}
#Test
public void testFind()
{
testList.insertFront(FOURTH);
testList.insertFront(THIRD);
testList.insertFront(SECOND);
testList.insertFront(FIRST);
String element = testList.findElement(SECOND);
assertNotNull("find failed, element null", element);
assertEquals(SECOND, element);
assertTrue("Find failed", findNode(testList, testList.findNode(SECOND)));
}
#Test
public void testRemove() throws NodeNotFoundException
{
testList.insertFront(FOURTH);
testList.insertFront(THIRD);
testList.insertFront(SECOND);
testList.insertFront(FIRST);
String second = testList.remove(SECOND);
assertNull("Found Second in list after removal", testList.findNode(SECOND));
assertEquals(SECOND, second);
}
#Test
public void testRemoveAll() throws NodeNotFoundException
{
testList.insertFront(FOURTH);
testList.insertFront(THIRD);
testList.insertFront(SECOND);
testList.insertFront(FIRST);
ArrayList<String> control = testList.convert();
ArrayList<String> result = testList.removeAll();
Iterator<String> iter = testList.iterator();
assertEquals(control, result);
assertFalse("RemoveAll Failed", iter.hasNext());
}
#Test
public void testSize()
{
assertEquals(0, testList.getSize());
testList.insertFront(FOURTH);
testList.insertFront(THIRD);
testList.insertFront(SECOND);
testList.insertFront(FIRST);
assertEquals(4, testList.getSize());
}
private static <T> boolean compareListToStrings(LinkedList<T> list, T... values)
{
int index = 0;
Iterator<T> iter = list.iterator();
while (iter.hasNext())
{
if (!values[index].equals(iter.next()))
{
return false;
}
index++;
}
return true;
}
private static <T> boolean findNode(LinkedList<T> list, LinkedList<T>.Node n)
{
Iterator<T> iter = list.iterator();
while (iter.hasNext())
{
if (n.getValue().equals(iter.next()))
{
return true;
}
}
return false;
}
private static void insertAll(LinkedList<String> list) throws NodeNotFoundException
{
list.removeAll();
list.insertFront(FOURTH);
list.insertFront(THIRD);
list.insertFront(SECOND);
list.insertFront(FIRST);
}
private static <T> void removeViaIterator(LinkedList<T> list) throws NodeNotFoundException
{
Iterator<T> iter = list.iterator();
while (iter.hasNext())
{
iter.next();
iter.remove();
}
}
}
the test class has 12 tests, and among them are the testIsEmpty and testFind.
When i ran the test, I failed these two tests.
i failed the testIsEmpty because of the last assert:
assertTrue("isEmpty Failed after emptying", testList.isEmpty());
The testFind failed because of this assert:
assertTrue("Find failed", findNode(testList, testList.findNode(SECOND)));
in the TestIsEmpty, I was thinking that I implemented the remove() function in the iterator class wrong, but I had no idea why. The testFind I looked at the function findNode() and I was pretty sure that there was nothing wrong with it.
It would be great if someone can check my code.
It seems when you are defining the iterator() in LinkedList you are creating a new LinkedList object instead of using the list you want to iterate on. So when you call Iterator iter = list.iterator() in removeViaIterator() method it is not returning any data and the while loop is not getting executed in the method.
Problems with findNode (actually, the iterator)
Sambaran spotted this one while I was typing...
LinkedList.iterator creates a new LinkedList every time it's called, and passes that to the LinkedListIterator constructor:
return new LinkedListIterator<T>(new LinkedList<T>());
The LinkedListIterator will always report (correctly) that the LinkedList is empty. That line should instead be:
return new LinkedListIterator<T>(this);
Problems with isEmpty (also the iterator)
The test is correct --- the list is not empty. LinkedListIterator.remove will never remove the first Node, so TestLinkedList.removeViaIterator will never completely empty a list.
Consider a LinkedListIterator traversing a LinkedList of only one Node: current points to the one and only Node, and previous points (by default) to null. After the iterator's next method is called once, current will point to the end-of-list null, while previous will point to the one and only Node... and it's now too late to remove the once-current Node.
current should not start at front, but in some illegal "pre-front" state; it should advance to front the first time next is called. Consider initializing it with a bogus Node that exists "before" the real list:
public class LinkedListIterator<T> implements Iterator<T>
{
final LinkedList<T> list;
LinkedList<T>.Node previous;
LinkedList<T>.Node current;
boolean canRemove;
public LinkedListIterator(LinkedList<T> list) {
// Required by remove.
this.list = list;
// Required by remove.
this.canRemove = false;
// No change, just making it explicit.
this.previous = null;
// Bogus "pre-front" Node, for before first call to next.
this.current = this.list.new Node(this.list.front, null);
}
// etc...
}
I added the list field so remove can handle the special case of removing front --- it has to update LinkedList.front instead of previous.next.
The canRemove is there to solve several other problems...
Problems with the Iterator contract
Your LinkedListIterator.next method should throw a NoSuchElementException when there is no next element. It should not return null, especially when null is a legal element value.
The remove method should raise an IllegalStateException in two situations:
[...] if the next method has not yet been called, or the remove method has already been called after the last call to the next method
That's from the Iterator interface's docs. You should (re-)read them carefully, because it's very easy to write an "Iteratorish" that works just well enough to make debugging everything else a nightmare.
The canRemove field can handle all of these cases. Initialize it to false. It is set to true by the next method (even if already true --- next doesn't care), and set to false again by the remove method. The only code that reads this is the remove method:
#Override
public void remove() {
if (!this.canRemove) {
throw new IllegalStateException("Helpful error message.");
}
// Remove current Node.
this.canRemove = false;
return;
}
Other Observations
Your JavaDocs are often outright wrong. For example, many of them claim the user can insert a Node into the list, when really the user inserts a type-T element. findNode, ironically, has the opposite problem: claiming that it returns an element, when it really returns a Node. (It doesn't help that there's a completely different findNode method in your test class.) I stopped reading your comments because they were so often misleading.
Your LinkedList can store null elements. This is OK, if awkward. What's not OK is that you often do something like if (someNode.getValue().equals(someElement)) { ... }, which will throw a NullPointerException if that node is storing a null.
findElement indicates success by returning the element found, and failure by returning null. But null is a legal element value, so findElement(null) will always return null. Does that mean you found it, or you didn't? Consider throwing a NodeNotFoundException (or ElementNotFoundException?) to indicate failure, like you do elsewhere. (As an aside, how is findElement different from contains?)
You often iterate through the whole list when you don't have to... and you duplicate much of the Iterator's code to do so. So use the Iterator! (...once it's fixed.)
You can just maintain a private int size field for use with getSize (and isEmpty), instead of counting all the elements in your list.
LinkedList.front should be private (with all the edits that implies).
That whole "Do not add the Node if it already exists in the list." thing is... not how lists usually behave. It's much more like a set, and a linked list is a terribly inefficient way to make a set.
Don't Repeat Yourself! Count the places you start at LinkedList.front and walk down the linked Nodes. Why not just call findNode or contains? Count the places you compare two Node elements, or (slightly different) determine if a could-be-null Node reference contains a known element. Replace that code with a private method or two, then use them. (That method would then handle the null elements I mentioned above, so you wouldn't have "if-null-else" sprinkled all over your code.)