Zach Cochran
by Zach Cochran
3 min read

Categories

Tags

Today I learned how to use ListIterators to iterate through collections.

At the end of yesterday’s blog I had mentioned that while doing my research into Iterators for the post that I had come across something called a ListIterator that provided a bit more functionality when iterating over a list. Today we’re going to take a look into ListItertors and the extra functionality that they do give us over normal Iterators.

Using ListIterators

There’s really not a lot of difference to the setup from normal Iterators. The only difference is going to be using a type of ListIterator instead of Iterator when declaring our variable:

       ListIterator<Product> iterator = products.listIterator();

Using the super basic example we had from last time, we can create our list, iterate through the items just like before, and print out their values:

   public void arrayListIterator() {
       List<Product> products = new ArrayList<>();
       products.add(ProductFixtures.table);
       products.add(ProductFixtures.chair);
       products.add(ProductFixtures.lamp);

       ListIterator iterator = products.listIterator();

       while (iterator.hasNext()) {
           System.out.println(iterator.next());
           iterator.remove();
       }

       System.out.println(products);
   }

Here’s a high level view of the extra methods that ListIterators open up for us:

  • hasPrevious() - Opposite of hasNext(), will see if an element still exists ahead of current location
  • previous() - Opposite of next(), will grab the previous element in the list
  • set() - Allows you to set the element at the current index to some other value
  • add() - Allows you to add an element at the current index (pushes end out by one)

Indexes

One of the big things that ListIterators give us is that we can actually see what the index is of where the iterator is currently pointing to.

For example, If I wanted to see what the current index of the array was, I could use either the nextIndex() or previousIndex() methods. Which one you use will be dependent on where it falls in relation to the next() call you make to grab the value. In the example below, you’ll see that calling nextIndex() before next() will get the current index, whereas calling it after will give you the next Index:

   public void arrayListIteratorIndex() {
       List<Product> products = new ArrayList<>();
       products.add(ProductFixtures.table);
       products.add(ProductFixtures.chair);
       products.add(ProductFixtures.lamp);

       ListIterator iterator = products.listIterator();

       while (iterator.hasNext()) {
           System.out.println(iterator.nextIndex());
           System.out.println(iterator.next());
           System.out.println(iterator.previousIndex());
       }
   }

Here’s the output from this:

0
net.fuzzylimes.blog.lists.Product{weightInLbs=45.0, name='Old Table'}
0
1
net.fuzzylimes.blog.lists.Product{weightInLbs=10.0, name='Hard Chair'}
1
2
net.fuzzylimes.blog.lists.Product{weightInLbs=5.0, name='Dim Lamp'}
2

Add and Set

These are pretty interesting methods as it allows you to take action as your iterating (similar to the remove).

In the case of set, you’re just going to be replacing the value of your current iterator. So in the example below, I have a List of three products. As I iterate through, I check to see if the product name is a chair. When it is, I set the value to be another table. In the end I verify that the final value of the products list has been substituted with a second table:

   @Test
   public void arrayListIteratorSet() {
       List<Product> products = new ArrayList<>();
       products.add(ProductFixtures.table);
       products.add(ProductFixtures.chair);
       products.add(ProductFixtures.lamp);

       ListIterator<Product> iterator = products.listIterator();

       while (iterator.hasNext()) {
           Product pro = iterator.next();
           if (pro.getName().equals(ProductFixtures.chair.getName())) {
               iterator.set(ProductFixtures.table);
           }
       }

       Assert.assertThat(products, Matchers.contains(ProductFixtures.table, ProductFixtures.table, ProductFixtures.lamp));
   }

And add is very similar to this as well. With add, we’re going to be putting in a new object into the List, but we’re going to be putting it right after the value we just accessed. So modifying the previous example, at the point where we find chair, using an add instead will cause us to insert a second table after the chair. Our final expected List will be table, chair, table, lamp:

   public void arrayListIteratorAdd() {
       List<Product> products = new ArrayList<>();
       products.add(ProductFixtures.table);
       products.add(ProductFixtures.chair);
       products.add(ProductFixtures.lamp);

       ListIterator<Product> iterator = products.listIterator();

       while (iterator.hasNext()) {
           Product pro = iterator.next();
           if (pro.getName().equals(ProductFixtures.chair.getName())) {
               iterator.add(ProductFixtures.table);
           }
       }

       System.out.println(products);
       Assert.assertThat(products, Matchers.contains(ProductFixtures.table, ProductFixtures.chair, ProductFixtures.table, ProductFixtures.lamp));
   }

Conclusion

This seems a bit more useful to me than the plain iterators, but I still am not sure on how often I’d use them. It seems potentially dangerous to be changing values as you’re iterating. I know I personally ran into several infinite loops while working through these sample examples between adding things that would forever trigger more additions, or using previous to forever reference the same value.

Hopefully you found this useful and were able to learn something along with me. As always, sample code from this post is available in my github repo.

💚 A.B.L.