I found there are two places which didn't deal with the edge cases mentioned by @Chris needed to improve, please see the comment.
public void insertLast(Object newValue) {
DSAListNode newNd;
newNd = new DSAListNode(newValue);
if (head == null) {
head = newNd;
tail = newNd; // this should be added
} else {
tail.next = newNd;
tail = newNd;
}
}
and this:
public Object removeFirst() {
Object nodeValue;
if (head == null) {
throw new IllegalArgumentException("head is empty");
} else {
nodeValue = head.getValue();
}
head = head.getNext();
// the following block should be added
if (head == null) {
tail = null;
}
return nodeValue;
}