Search code examples
javaiteratorlistiterator

Remove entries of multiple lists with one iterator


I have 2 int Lists A, B and 1 String List C. They all have the same length. I want to look through C and remove all empty string in addition to the entries of A and B, with the same index. So for example:

A:[1, 2, 3]                             A:[1, 3]

B:[4, 5, 6]        should turn into     B:[4, 6]

C:["C", "", B"]                         C:["C", "B"]

My current code looks like this:

int i = 0;
for (Iterator<String> iterator = C.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
        A.remove(i);
        B.remove(i);
    }
    i++;
}

But that doesn't work.


Solution

  • You can avoid indices altogether and just iterate over all three lists at the same time:

    List<Integer> a = new ArrayList<Integer>(Arrays.asList(1,2,3));
    List<Integer> b = new ArrayList<Integer>(Arrays.asList(4,5,6));
    List<String> c = new ArrayList<String>(Arrays.asList("a","","c"));
    
    Iterator<Integer> a_iterator = a.iterator();
    Iterator<Integer> b_iterator = b.iterator();
    Iterator<String> c_iterator = c.iterator();
    
    while (c_iterator.hasNext() && b_iterator.hasNext() && a_iterator.hasNext()){
       a_iterator.next();
       b_iterator.next();
       String cString = c_iterator.next();
       if (cString == null || cString.isEmpty()){
         a_iterator.remove();
         b_iterator.remove();
         c_iterator.remove();
       }
    }
    

    That said, I'd try to avoid dealing with them in separate lists at all. Trying to keep the iterators and indices straight is error prone. Instead, I would favor having a single list with a more complex object to hold the associated Integers and String:

    public class ABC {
    
    private final Integer a;
    private final Integer b;
    private final String c;
    
    private ABC(Integer a, Integer b, String c){
        this.a = a;
        this.b = b;
        this.c = c;
    }
    
    // Add Getters                                                                                                                                                                   
    

    }

    Then I can just have List<ABC> and I always know that I have the right 'b' and 'c' associated with each 'a' and so forth. You can iterate over that list and remove the entire ABC that has a blank c. If I control the code that produces the lists, I'd skip directly to List<ABC>. If they're given to me like this, I'd probably still combine them, but I may just deal with them as-is if they're not used much. In general, though, I don't like to have to write code to maintain the implied relationship between lists.