0

I have a class which is basically wrapper around a Map (that also holds some business logic obviously). What I would like to be able to do is this:

for(Object o: instanceOfMyClass) { ... }

So I want to loop over the values of the Map inside my class. Which interfaces do I need to implement in my class (Iterator, Iterable, ...)? I guess in the interface to implement I somehow need to return an Interator; how can I "reuse" the iterator for the Map (through Map.entrySet()) keeping in mind that I only want to have the values exposed in the iterator of my class?

Thanks a lot!

Daniel
  • 2,409
  • 2
  • 26
  • 42
  • Possible duplicate of [How does the Java 'for each' loop work?](https://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work) – Andrey Tyukin Feb 09 '18 at 10:15
  • Possible duplicate of [How to efficiently iterate over each entry in a 'Map'?](https://stackoverflow.com/questions/46898/how-to-efficiently-iterate-over-each-entry-in-a-map) – Ben Thurley Feb 09 '18 at 10:33
  • Iterating over a Map is a bit different to iterating over a List. – Ben Thurley Feb 09 '18 at 10:34

2 Answers2

2

It's as simple as implementing Iterable. In your case, you want to implement Iterable<SomeType>:

public class Main implements Iterable<String>
{
    private final Map<String, String> myMap = new HashMap<>();
    {
        myMap.put("hello", "world");
        myMap.put("aaa", "bbb");
    }

    @Override
    public Iterator<String> iterator()
    {
        return Collections.unmodifiableMap(myMap).values().iterator();
    }
}

Here's a test method, with the output below:

public static void main(String... args)
{
    for (String entry : new Main())
    {
        System.out.println("Value: " + entry);
    }
}

Value: bbb
Value: world

Michael
  • 41,989
  • 11
  • 82
  • 128
  • Argh, you are too fast ! I doesn't have the time to write a complete response :p. – Valentin Michalak Feb 09 '18 at 10:22
  • 1
    @ValentinMichalak [cumonnn](http://i0.kym-cdn.com/entries/icons/original/000/009/798/sanichedgehog.jpg) – Michael Feb 09 '18 at 10:23
  • First of all: thanks for the fast response! But I would like to loop over the VALUE of the Map/Entry only - not the whole Entry. (The key is only used internally for some specific logic and not of interest to the consumer of the class). Any suggestions on this? – Daniel Feb 09 '18 at 10:26
  • @Daniel then return `values().iterator()` instead of `entrySet().iterator()`. – Kayaman Feb 09 '18 at 10:30
  • @Daniel No problem. I've edited my answer. The concept's the same. – Michael Feb 09 '18 at 10:31
1

Implementing Iterable is what allows you to use the for(Foo f : foo) syntax, but if you're wrapping a Map you might want to implement the Map.forEach() method instead. It's a little bit nicer when you can directly address the key and value separately (although the question is now about accessing values only).

// Mostly copied from Map.forEach()
// Adjust generic parameters if necessary
public void forEach(BiConsumer<? super K, ? super V> action) {
    Objects.requireNonNull(action);
    for (Map.Entry<K, V> entry : internalMap.entrySet()) {
        K k;
        V v;
        try {
            k = entry.getKey();
            v = entry.getValue();
        } catch(IllegalStateException ise) {
            // this usually means the entry is no longer in the map.
            throw new ConcurrentModificationException(ise);
        }
        action.accept(k, v);
    }
}

If the default implementation is enough, you can of course just delegate to internalMap.forEach(action);.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • I don't understand. Why do you need to copy the method? Why can't you just defer the call? i.e. `internalMap.forEach(action);` – Michael Feb 09 '18 at 10:35
  • @Michael it's more in line with the other answer. Your class wraps a `Map` and you may want different behaviour than just the default `Map.forEach`. Of course delegating the call works fine if you don't need anything special, but there's the implementation in case you do. – Kayaman Feb 09 '18 at 10:40