-
-
Notifications
You must be signed in to change notification settings - Fork 27.3k
#778: Binary Search Tree Iterator #779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
5726874 to
d32cb41
Compare
… integration tests. Refactored file structure to be friendly to future contributors with iterators of more data structures. Added JUnitPlatform to enable running test suite across all iterator implementations. Added README to /binarysearchtree to document what it does and how it works.
d32cb41 to
3e0cfa5
Compare
|
@mitchellirvin Thanks for the PR 👍 I have started the review. Will complete it by tomorrow. |
|
@mitchellirvin this looks great 👍 |
npathai
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have my initial review. Let me know once you have made required changes or have any questions.
| * expect to retrieve TreeNodes according to the Integer's natural ordering (1, 2, 3...) | ||
| * @param <T> This Iterator has been implemented with generic typing to allow traversal of TreeNodes of various types | ||
| */ | ||
| public class BstIterator<T> implements Iterator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iterator is used as raw type here, change it to Iterator<TreeNode<T>>
| import java.util.ArrayDeque; | ||
|
|
||
| /** | ||
| * This BstIterator iterates IN order. For example, given a BST with Integer values, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The writeup of javadoc should be changed to something like
An in-order implementation of BST iterator.
| /** | ||
| * This BstIterator iterates IN order. For example, given a BST with Integer values, | ||
| * expect to retrieve TreeNodes according to the Integer's natural ordering (1, 2, 3...) | ||
| * @param <T> This Iterator has been implemented with generic typing to allow traversal of TreeNodes of various types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion:
This Iterator has been implemented with generic typing to allow traversal of TreeNodes of various types
I guess the statement should be "it allows TreeNodes with different value types." Or something on that line.
| @Override | ||
| public TreeNode<T> next() throws IllegalStateException { | ||
| if (pathStack.isEmpty()) { | ||
| throw new IllegalStateException(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change the exception to NoSuchElementException
| */ | ||
| public TreeNode(T val) { | ||
| this.val = val; | ||
| this.left = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to explicitly initialize class properties to null.
| @BeforeAll | ||
| void createTrees() { | ||
| nonEmptyRoot = new TreeNode(Integer.valueOf(5)); | ||
| nonEmptyRoot.setLeft(new TreeNode(3)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are lot of raw type warnings wherever new object of TreeNode is being created, it is because of raw types. TreeNode is generic but it is being used in raw manner. So change creation of TreeNode to new TreeNode<>(val)
| void nextAndHasNextAcrossEntirePopulatedTree() { | ||
| BstIterator iter = new BstIterator(nonEmptyRoot); | ||
| assertTrue(iter.hasNext(), "Iterator hasNext() should be true."); | ||
| assertEquals(iter.next().getVal(), 1, "First Node is 1."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At all places where assertEquals is being used the order of arguments is incorrect. assertEquals(expected, actual) should be the order of arguments
| public class TreeNodeTest<T> { | ||
|
|
||
| @Test | ||
| void treeNodeGetters() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TreeNodeTest should not be part of unit testing. The setters and getters are being tested indirectly by BSTIteratorTest already.
Testing beans is not a good practice. Yes there are exceptions to this, if beans have validation logic then it makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appreciate the feedback! Wasn't sure if I needed this to ensure code coverage or not.
| This means that given a binary search tree like the following, the iterator would | ||
| return values in order: 1, 3, 4, 6, 7, 8, 10, 13, 14. | ||
|
|
||
|  |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we reduce the image size a bit? Its quiet big
| * access the container's elements. The Iterator pattern decouples algorithms from containers. | ||
| * <p> | ||
| * In this example the Iterator ({@link ItemIterator}) adds abstraction layer on top of a collection | ||
| * In this example the Iterator ({@link Iterator}) adds abstraction layer on top of a collection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While investigating this change I found out that the current TreasureChestIterator implementation is incorrect. It is making assumptions about the underlying datastructure being ArrayList. And if it was changed to LinkedList, then iterator would perform really badly, which violates the whole purpose of iterator. So will have to investigate how to rectify that.
|
@iluwatar There are a few things that I needed your help with.
|
| * | ||
| * Runs a test suite containing all tests within the com.iluwatar.iterator package | ||
| */ | ||
| @RunWith(JUnitPlatform.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does adding JUnitPlatform dependency help? I haven't switched to using Junit 5 yet, hence the question.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the limited experience I have with it, I've found it's a great tool to define and run suites of tests instead of individual test classes one at a time. If you want to run all the tests for the iterator package you can run the AppTest class instead of manually running each *Test class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am still unsure about this change. I read about JUnitPlatform and I understand that it makes running all test cases simple. But AppTest wasn't meant for that. It tests App class and it's not being run currently.
Here is what we can do, we can revert this change and raise an issue to discuss about having AllTests class in every pattern. Till then I am afraid you will have to revert this change. Let me know what are your thoughts.
|
@npathai committed the requested changes. left the explicit null assignments in the TreeNode constructor because of "Clean Code"'s preference for the explicit over the implicit. |
mitchellirvin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requested changes made and committed, build passing
| /** | ||
| * | ||
| * @return TreeNode next. The next element according to our in-order traversal of the given BST | ||
| * @throws IllegalStateException if this iterator does not have a next element |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Javadoc needs to be updated accordingly.
| * | ||
| * Runs a test suite containing all tests within the com.iluwatar.iterator package | ||
| */ | ||
| @RunWith(JUnitPlatform.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am still unsure about this change. I read about JUnitPlatform and I understand that it makes running all test cases simple. But AppTest wasn't meant for that. It tests App class and it's not being run currently.
Here is what we can do, we can revert this change and raise an issue to discuss about having AllTests class in every pattern. Till then I am afraid you will have to revert this change. Let me know what are your thoughts.
759cb8f to
7610980
Compare
|
@npathai I agree with removing AppTest and JUnitPlatform after further research. I've committed the changes (along with the updated javadoc). |
|
@mitchellirvin Looks good.
|
7610980 to
3971dfb
Compare
|
@npathai Roger that! Should have them in within the next several days. |
3971dfb to
363ea63
Compare
…e each implementation of the Iterator interface. Removed the redundant ItemIterator interface. Added insert() method to TreeNode class to allow for more elegant construction of BSTs.
363ea63 to
1c2ddfa
Compare
|
@npathai Sorry for the delay, ended up doing more refactoring than originally planned. Should be good for a final review. Let me know what you think of the changes? Tried hard to make the code clean and readable. |
npathai
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick little change needed. Otherwise it's 👍
| } | ||
|
|
||
| ItemIterator iterator(ItemType itemType) { | ||
| public TreasureChestItemIterator iterator(ItemType itemType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return type should be changed to Iterator
| public void testIterator(Item expectedItem) { | ||
| final TreasureChest chest = new TreasureChest(); | ||
| final ItemIterator iterator = chest.iterator(expectedItem.getType()); | ||
| final TreasureChestItemIterator iterator = chest.iterator(expectedItem.getType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same change here. You being the client of TreasureChest don't care if the concrete implementation of Iterator is TreasureChestItemIterator or something else. You just need an Iterator. This allows for easier refactoring as you can freely change name of the class and client code will not be affected. I hope that makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, crap. Good catch!
|
@npathai should be good now. Thanks for all your help in this review! |
npathai
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how I missed these changes before. Caught my eye when I saw unchecked cast to Item in test case. Apologies.
| private static void demonstrateTreasureChestIteratorForType(ItemType itemType) { | ||
| LOGGER.info("------------------------"); | ||
| LOGGER.info("Item Iterator for ItemType " + itemType + ": "); | ||
| Iterator itemIterator = TREASURE_CHEST.iterator(itemType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raw type: Iterator<Item> should be returned from TREASURE_CHEST.iterator()
| } | ||
|
|
||
| ItemIterator iterator(ItemType itemType) { | ||
| public Iterator iterator(ItemType itemType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return type should not be raw, it should be Iterator<Item>. Previously this was not needed because there was a dedicated interface called ItemIterator which only worked for Items
| * | ||
| */ | ||
| public class TreasureChestItemIterator implements ItemIterator { | ||
| public class TreasureChestItemIterator implements Iterator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raw type: it should be implements Iterator<Item>
| public void testIterator(Item expectedItem) { | ||
| final TreasureChest chest = new TreasureChest(); | ||
| final ItemIterator iterator = chest.iterator(expectedItem.getType()); | ||
| final Iterator iterator = chest.iterator(expectedItem.getType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raw type: Iterator<Item>
|
|
||
| while (iterator.hasNext()) { | ||
| final Item item = iterator.next(); | ||
| final Item item = (Item) iterator.next(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for cast now, it was needed because of raw types. It will be removed now.
|
@mitchellirvin It's done. Thanks 👍 |
In a nutshell: Implemented a BSTIterator that iterates in-order, given a root node. README.md in /binarysearchtree discusses the trivial solution, and an optimal solution (the one implemented in this PR) in depth.
Included comprehensive unit and integration tests. Refactored file structure to be friendly to future contributors with iterators of other data structures. Added JUnitPlatform to enable running test suite across all iterator implementations. Added README to /binarysearchtree to document what it does and how it works.
This is my first PR against an open source project, would love feedback!