Your question is a good beginners question :)
I have written about it here.
I guess the biggest thing to keep in mind is that the Iterable is not backed by a collection, it is computed on the fly as and when the next() method is invoked. Just keep this in mind.
Once your done with above post if your the i want to see the code kind of person.
// Line number157
if (hasMore) {
nextKey = input.getKey();
nextKeyIsSame = comparator.compare(currentRawKey.getBytes(), 0,
currentRawKey.getLength(),
nextKey.getData(),
nextKey.getPosition(),
nextKey.getLength() - nextKey.getPosition()
) == 0;
} else {
nextKeyIsSame = false;
}
This is a snippet from ReduceContextImpl
The method gets called each time, you invoke next(), it basically checks if the key is changing in the underlying stream if not it just passes you the next value (remember keys are ordered), else it makes arrangements to call the reducer method again with a new key and iterable.
The underlying stream is always a key, value pair, the ReducerContextImpl gives you an illusion/abstraction of it being a key,Collection pair.
Like i said at the start ....
The biggest thing to keep in mind is that the Iterable is not backed by a collection, it is computed on the fly as and when the next() method is invoked. Just keep this in mind.
This theme is common across the MapReduce framework, all computations are done on streams nothing is ever loaded entirely in the memory, it took me a while to get this :) hence the eagerness to share it.