22

I'm trying to remove all Strings that are of even length in a set. Here is my code so far, but I am having trouble getting the index from the iterator in the enhanced-for-loop.

public static void removeEvenLength(Set<String> list) {
    for (String s : list) {
        if (s.length() % 2 == 0) {
            list.remove(s);
        }
    }
}
wassgren
  • 18,651
  • 6
  • 63
  • 77
f3d0r
  • 541
  • 3
  • 12
  • 27
  • 4
    `getting the index`: there are none, as a Set is not ordered. – njzk2 Jan 27 '15 at 19:40
  • 3
    as a side note, your `Set` is named `list`, which is confusing, given that `List` is the name of another `Collection`, but an ordered one (which has indexes.) – njzk2 Jan 27 '15 at 19:42

4 Answers4

31

A Set has no concept of an index of an element. The elements have no order in the set. Moreover, you should use an Iterator when iterating to avoid a ConcurrentModificationException when removing an element from a collection while looping over it:

for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String s =  iterator.next();
    if (s.length() % 2 == 0) {
        iterator.remove();
    }       
}

Note the call to Iterator.remove() instead of Set.remove().

M A
  • 71,713
  • 13
  • 134
  • 174
  • any argument in favor of the `for` loop instead of the `while` loop here? – njzk2 Jan 27 '15 at 19:41
  • 4
    Not really, I just used a `for` since the OP is already using one. A `while` loop would work but you would make the scope of the `Iterator` outside the `for` loop. – M A Jan 27 '15 at 19:46
19

Java 8 has introduced Collection.removeIf(), which allows you to do:

set.removeIf(s -> s.length() % 2 == 0)
Leonardo Lima
  • 626
  • 5
  • 5
  • 1
    I want to point out that it is `->`, not `=>`. And, it is just a syntax sugar of "iterating and remove with iterator", according to the JavaDoc. – WesternGun Feb 21 '19 at 20:55
  • @WesternGun nice catch, been doing too much TS lately. Yes, the implementation is almost the same as some of the answers above, as can be seen [here](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/Collection.java#l409). You just write less code. – Leonardo Lima Feb 22 '19 at 10:45
  • 1
    very sexy solution :) – Samy Omar Jul 15 '20 at 22:19
8

Just thought that I'd post a Java 8 solution that may help someone in the future. Java 8 Streams offers a bunch of nice methods such as filter and collect. The filter method simply filters out the elements from the stream that should be carried on to the next step. The collect method combines elements to a Collection of some sort or a Map.

// The data to filter
final Set<String> strings = 
        new HashSet<>(Arrays.asList("a", "ab", "abc", "abcd"));

// Now, stream it!
final Set<String> odds =
        strings.stream()
               .filter(s -> s.length() % 2 != 0) // keep the odds
               .collect(Collectors.toSet());     // collect to a new set

This does not actually modify the original collection but creates a new Set containing the String objects of odd length.

For more reading on Java 8 Streams, checkout this excellent tutorial from Oracle or the great JavaDocs.

wassgren
  • 18,651
  • 6
  • 63
  • 77
  • Good choice when we need to modify the Many side of a `@OneToMany` relationship, deleting a child; need to create a new `Set` and set to the One side. – WesternGun Feb 21 '19 at 20:49
4

You don't need the index. But you do need the explicit Iterator. The iterator has the remove() method, no parameters, that removes the current item from the collection.

Iterator<String> itr = list.iterator();  // list is a Set<String>!
while (itr.hasNext())
{
    String s = itr.next();
    if (s.length() % 2 == 0) {
        itr.remove();
    }
}
rgettman
  • 176,041
  • 30
  • 275
  • 357