default <T> Collection<T> delegateCollection(Iterable<M> spaces,
			Function<M, Collection<T>> func) {
		return new AbstractCollection<>() {
			@Override
			public Iterator<T> iterator() {
				return NestedIterator.start(spaces.iterator(), func.andThen(Iterable::iterator));
			}

			@Override
			public int size() {
				try (LockHold hold = LockHold.lock(readLock())) {
					int sum = 0;
					for (M m : spaces) {
						sum += func.apply(m).size();
					}
					return sum;
				}
			}

			public boolean isEmpty() {
				try (LockHold hold = LockHold.lock(readLock())) {
					for (M m : spaces) {
						if (!func.apply(m).isEmpty()) {
							return false;
						}
					}
					return true;
				}
			}
		};
	}
Compose a collection, lazily, from collections returned by delegates

@param spaces the delegates
@param func a collection getter for each delegate
@return the lazy catenated collection
Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/space/DBTraceDelegatingManager.java