@Override
    public boolean hasNext() {
      // return previous collection of values if not over
      if (valuesIterator != null && valuesIterator.hasNext()) {
        return true;
      }
      // sets the next values iterator
      if (mapIterator == null) {
        mapIterator = map.entrySet().iterator();
      }
      if (mapIterator.hasNext()) {
        // set the next entry in the map as current
        Map.Entry currentMapEntry = mapIterator.next();
        // set the index key
        currKey = currentMapEntry.getKey();
        // if the index key in currentIndexEntry is present in the
        // keysToRemove collection or is Undefined or Null
        // skip the current map entry and advance to the
        // next entry in the index map.
        // skipping null & undefined is required so that they do not get
        // into results of range queries.
        // equality and not equality for null/undefined would create
        // valuesIterator so it would not come here
        if (currKey != indexKey
            && (currKey == QueryService.UNDEFINED || currKey == IndexManager.NULL
                || keysToRemove != null && removeFromKeysToRemove(keysToRemove, currKey))) {
          return hasNext();
        }
        // iterator on the collection of values for the index key
        Object values = currentMapEntry.getValue();
        if (values instanceof Collection) {
          valuesIterator = ((Collection) values).iterator();
        } else {
          valuesIterator = null;
          currValue = values;
        }
        return values != null && (values instanceof RegionEntry || valuesIterator.hasNext());
      }

      currKey = null;
      return false;
    }
This iterator iterates over the CSL map as well as on the collection of values for each entry
in the map. If the map has next element, check if the previous valuesIterator (iterator on
the Collection) has been created or has finished iterating. If not created, create an
iterator on the Collection. If created and not finished, return, so that the next() call
would create a new InMemoryIndexStorageEntry for the current key and next RE from the
collection of values.
geode-core/src/main/java/org/apache/geode/cache/query/internal/index/MemoryIndexStore.java