code: public ICacheKeyGenerator combine(ICacheKeyGenerator otherKeyGen) {
  		if (this.equals(otherKeyGen)) {
  			return this;
  		}
  		@SuppressWarnings("unchecked")
  		AbstractCollectionCacheKeyGenerator<T> other = (AbstractCollectionCacheKeyGenerator<T>)otherKeyGen;
  		if (isProvisional() && other.isProvisional()) {
  			// should never happen
  			throw new IllegalStateException();
  		}
  		// If either generator is provisional, return a provisional result
  		if (isProvisional()) {
  			return other;
  		} else if (other.isProvisional()) {
  			return this;
  		}
  		if (getCollection() == null) {
  			return this;
  		}
  		if (other.getCollection() == null) {
  			return other;
  		}
  		// See if one of the keygens encompasses the other.  This is the most likely
  		// case and is more performant than always creating a new keygen.
  		int size = getCollection().size(), otherSize = other.getCollection().size();
  		if (size > otherSize && getCollection().containsAll(other.getCollection())) {
  			return this;
  		}
  		if (otherSize > size && other.getCollection().containsAll(getCollection())) {
  			return other;
  		}
  		// Neither keygen encompasses the other, so create a new one that is a combination
  		// of the both of them.
  		Set<T> combined = new HashSet<T>();
  		combined.addAll(getCollection());
  		combined.addAll(other.getCollection());
  		return newKeyGen(combined, false);
  	}
docs: Returns a cache key generator that is the combination of this cache key generator and the
  specified cache key generator (i.e. the cache keys generated by the returned object vary
  according to the conditions honored by this generator and the specified generator.
  
  @param otherKeyGen
  the key generator to combine with this key generator
  
  @return the combined cache key generator. Can be this object or {@code otherKeyGen} if either
  one already encompasses the other.