code: public int ensureSparseFeature(String featureName, String index) {
          ensureFeature(featureName);
          ObjectIntMap<String> sparseIndex = sparseFeatureIndex.get(featureName);
          IntObjectMap<String> reverseSparseIndex = reverseSparseFeatureIndex.get(featureName);
          if (sparseIndex == null || reverseSparseIndex == null) {
              synchronized (sparseFeatureIndex) {
                  sparseIndex = sparseFeatureIndex.get(featureName);
                  reverseSparseIndex = reverseSparseFeatureIndex.get(featureName);
                  if (sparseIndex == null || reverseSparseIndex == null) {
                      sparseIndex = new ObjectIntHashMap<>();
                      reverseSparseIndex = new IntObjectHashMap<>();
                      sparseFeatureIndex.put(featureName, sparseIndex);
                      reverseSparseFeatureIndex.put(featureName, reverseSparseIndex);
                  }
              }
          }
          Integer rtn = sparseIndex.getOrDefault(index, -1);
          if (rtn == -1) {
              //noinspection SynchronizationOnLocalVariableOrMethodParameter
              synchronized (sparseIndex) {
                  rtn = sparseIndex.getOrDefault(index, -1);
                  if (rtn == -1) {
                      rtn = sparseIndex.size();
                      reverseSparseIndex.put(rtn, index);
                      sparseIndex.put(index, rtn);
                  }
              }
          }
          return rtn;
      }
docs: An optimization, this lets clients inform the ConcatVectorNamespace of how many sparse feature components to
  expect, again so that we can avoid resizing ConcatVectors.
  @param featureName the feature to use in our index
  @param index the sparse value to ensure is available