我有一个实现可迭代接口的LinkedList类,在LinkedList类中我有一个内部节点类。我有另一个运行JUnit 4的TestLinkedList类。测试类将检查linkedlist类中的所有函数。
这是我的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>());
}
}
这是我的LinkedListIterator类:
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());
}
}
这是我的TestLinkedList类:
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();
}
}
}
测试类有12个测试,其中包括testIsEmpty和testFind。 当我进行测试时,我没有通过这两项测试。 我因为上一个断言而失败了testIsEmpty:
assertTrue("isEmpty Failed after emptying", testList.isEmpty());
由于这个断言,testFind失败了:
assertTrue("Find failed", findNode(testList, testList.findNode(SECOND)));
在TestIsEmpty中,我以为我在迭代器类中实现了remove()函数错误,但我不知道为什么。 testFind我看了函数findNode(),我很确定它没有任何问题。
如果有人可以查看我的代码,那就太好了。
答案 0 :(得分:1)
在LinkedList中定义iterator()时,您似乎正在创建一个新的LinkedList对象,而不是使用您想要迭代的列表。因此,当您在removeViaIterator()方法中调用Iterator iter = list.iterator()时,它不返回任何数据,并且未在方法中执行while循环。
答案 1 :(得分:0)
findNode
(实际上是迭代器)
LinkedList.iterator
每次调用时都会创建 new LinkedList
,并将 传递给LinkedListIterator
构造函数:
return new LinkedListIterator<T>(new LinkedList<T>());
LinkedListIterator
将始终(正确地)报告LinkedList
为空。该行应改为:
return new LinkedListIterator<T>(this);
isEmpty
的问题(也是迭代器)
测试正确---列表不为空。 LinkedListIterator.remove
永远不会删除第一个Node
,因此TestLinkedList.removeViaIterator
永远不会完全清空列表。
考虑LinkedListIterator
遍历仅LinkedList
Node
current
个Node
点到previous
和null
点(默认情况下)next
。在调用迭代器的current
方法一次后,null
将指向列表末尾previous
,而Node
将指向唯一Node
current
} ...现在要删除一次当前的front
。
front
不应该从front
开始,而是在一些非法的“next
之前”状态;它应该在第一次调用Node
时提前到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...
}
。考虑使用“真实列表之前”存在的虚假list
初始化它:
remove
我添加了front
字段,因此LinkedList.front
可以处理删除previous.next
的特殊情况---它必须更新canRemove
而不是Iterator
。
LinkedListIterator.next
可以解决其他几个问题......
NoSuchElementException
合同问题
当没有下一个元素时,您的null
方法应该抛出null
。当remove
是合法元素值时,它应该不返回IllegalStateException
,尤其是。
next
方法应在两种情况下引发remove
:
[...]如果尚未调用
next
方法,或者在上次调用canRemove
方法后已调用false
方法
那来自Iterator
interface's docs。你应该(重新)仔细阅读它们,因为写一个“Iteratorish”非常容易,它可以很好地调试所有 else 是一场噩梦。
true
字段可以处理所有这些情况。将其初始化为next
。它由true
方法设置为next
(即使已经false
--- remove
无关心),并再次设置为remove
@Override
public void remove() {
if (!this.canRemove) {
throw new IllegalStateException("Helpful error message.");
}
// Remove current Node.
this.canRemove = false;
return;
}
方法。 读取的唯一代码是Node
方法:
T
其他观察
您的JavaDoc通常是完全错误的。例如,他们中的许多人声称,当用户真正插入类型 - findNode
元素时,用户可以在列表中插入Node
。具有讽刺意味的是,findNode
有相反的问题:声称它返回一个元素,当它真正返回LinkedList
时。 (在您的测试类中有一个完全不同的 null
方法没有帮助。)我停止阅读您的评论,因为它们经常被误导。
您的if (someNode.getValue().equals(someElement)) { ... }
可以存储NullPointerException
元素。这很好,如果尴尬的话。什么是不好的是,您经常执行类似null
的操作,如果该节点存储findElement
,则会抛出null
。
null
表示通过返回找到的元素来表示成功,并通过返回findElement(null)
来表示失败。但null
是一个合法的元素值,因此NodeNotFoundException
总是返回ElementNotFoundException
。这是否意味着你找到了它,或者你没有找到它?考虑抛出findElement
(或contains
?)来表示失败,就像你在别处做的那样。 (另外,Iterator
与Iterator
的区别如何?)
当您不必执行时,您经常遍历整个列表...并且您复制了大部分private int size
代码。所以使用getSize
! (......一旦修好了。)
您可以维护一个isEmpty
字段,以便与LinkedList.front
(和private
)一起使用,而不是计算列表中的所有元素。
LinkedList.front
应为Node
(包含所暗示的所有修改)。
整个“如果节点已存在于列表中,请不要添加节点。”事情是......而不是列表通常如何表现。它更像是一个集合,而链表是制作集合的一种非常低效的方式。
Don't Repeat Yourself!计算从findNode
开始的地点,然后沿着链接的contains
向下走。为什么不拨打Node
或Node
?计算比较两个null
元素的位置,或(稍微不同)确定一个可能为空的{{1}}引用是否包含已知元素。用一两个私有方法替换该代码,然后使用它们。 (那个方法会处理我上面提到的{{1}}元素,所以你不会在你的代码中撒上“if-null-else”。)