Reputation: 439
Context: I created a BinarySearchTree class in Java as a learning exercise since I'm new to Java. I'm currently writing a method for a level-order traversal (BFS) that returns a list of lists of node values for each level, where the top-level list index represents the level number and the lower-level list at each index contains the node values for that level, e.g. for this tree
F
/ \
/ \
/ \
B G
/ \ \
/ \ \
A D I
/ \ /
C E H
The levelOrder() method would return
[[F], [B, G], [A, D, I], [C, E, H]]
Problem: In the implementation, I declare a top-level list called 'listOfLevels' to hold the lists of levels and a lower-level list called 'level' to store the nodes at each level (which I intended to clear and reuse across levels). However, I noticed that if I called the List.clear() method on 'level' after adding it to 'listOfLevels', I would get empty lists inside 'listOfLevels' for the method's return value, like this
[[], [], [], []]
I'm not sure why this happens. According to the ArrayList documentation, "As elements are added to an ArrayList, its capacity grows automatically," so I figured adding node values from the next level would show up in the list after clearing the previous level's values using the clear() method. This didn't seem to work, though. I fixed it by explicitly allocating a new ArrayList for 'level' each time instead.
Main Question: If an ArrayList grows automatically as elements are added, why do I need to allocate a new ArrayList each time? Why does calling clear() between levels result in empty lists for the top-level list?
Here are the relevant bits from the Binary Search Tree class:
public BinarySearchTree<K extends Comparable<K>, V> {
private Node<K,V> root;
...
public List<List<V>> levelOrder() {
// see implementation below
}
...
private static class Node<K extends Comparable<K>, V> {
private K key;
private V value;
private Node<K,V> left;
private Node<K,V> right;
...
}
}
And here is the levelOrder() method implementation
public List<List<V>> levelOrder() {
Queue<Node<K,V>> queue = new LinkedList<Node<K,V>>();
List<List<V>> listOfLevels = new ArrayList<List<V>>();
List<V> level = new ArrayList<V>();
int currLevelCount = 1, nextLevelCount = 0;
queue.add(root);
while (!queue.isEmpty()) {
Node<K,V> node = queue.remove();
currLevelCount--;
level.add(node.value);
if (node.hasLeft()) {
queue.add(node.left);
nextLevelCount++;
}
if (node.hasRight()) {
queue.add(node.right);
nextLevelCount++;
}
if (currLevelCount == 0) {
listOfLevels.add(level);
//level.clear(); <-- why doesn't this work?
level = new ArrayList<V>();
currLevelCount = nextLevelCount;
nextLevelCount = 0;
}
}
return listOfLevels;
}
Upvotes: 0
Views: 881
Reputation: 19
When you save your ArrayList to your List of ArrayLists you are not making a copy so it is saving the same variable. This is because an ArrayList is a pointer to a spot in memory; this is what is actually being saved. By clearing the ArrayList it clears the memory which also affects the saved memory location in your ArrayList of Lists. Use your new line instead of the clear.
Upvotes: 1
Reputation: 31279
If you call clear
on the level
list, you are clearing the list that you have just added to the listOfLevels
, and then you start to re-use it.
Since you never allocate a new level
list with new ArrayList()
when you call clear
instead of creating a new arraylist, you are effectively using the same list over and over again.
And since you are clearing it every time, you will never have an values in the lists under listOfLevels
.
Calling new ArrayList
instead of clear
is indeed the correct solution.
Upvotes: 1