diff --git a/core/src/main/java/org/jboss/jandex/AnnotationInstance.java b/core/src/main/java/org/jboss/jandex/AnnotationInstance.java
index 87443da9..29d45766 100644
--- a/core/src/main/java/org/jboss/jandex/AnnotationInstance.java
+++ b/core/src/main/java/org/jboss/jandex/AnnotationInstance.java
@@ -29,7 +29,7 @@
/**
* An annotation instance represents a specific usage of an annotation on a
- * target. It contains a set of values, as well as a reference to the target
+ * target. It contains a set of members, as well as a reference to the target
* itself (e.g. class, field, method, etc).
*
*
@@ -131,12 +131,12 @@ public static AnnotationInstanceBuilder builder(DotName annotationType, boolean
}
/**
- * Construct a new mock annotation instance. The passed values array will be defensively copied.
+ * Construct a new mock annotation instance. The passed {@code values} array will be defensively copied.
* It is assumed that the annotation is {@link #runtimeVisible()}.
*
* @param name the name of the annotation instance
* @param target the thing the annotation is declared on
- * @param values the values of this annotation instance
+ * @param values the members of this annotation instance
* @return the new mock Annotation Instance
*/
public static AnnotationInstance create(DotName name, AnnotationTarget target, AnnotationValue[] values) {
@@ -144,12 +144,12 @@ public static AnnotationInstance create(DotName name, AnnotationTarget target, A
}
/**
- * Construct a new mock annotation instance. The passed values array will be defensively copied.
+ * Construct a new mock annotation instance. The passed {@code values} array will be defensively copied.
*
* @param name the name of the annotation instance
* @param visible whether the annotation is visible at runtime via the reflection API
* @param target the thing the annotation is declared on
- * @param values the values of this annotation instance
+ * @param values the members of this annotation instance
* @return the new mock Annotation Instance
*/
public static AnnotationInstance create(DotName name, boolean visible, AnnotationTarget target,
@@ -173,12 +173,12 @@ public int compare(AnnotationValue o1, AnnotationValue o2) {
}
/**
- * Construct a new mock annotation instance. The passed values list will be defensively copied.
+ * Construct a new mock annotation instance. The passed {@code values} list will be defensively copied.
* It is assumed that the annotation is {@link #runtimeVisible()}.
*
* @param name the name of the annotation instance
* @param target the thing the annotation is declared on
- * @param values the values of this annotation instance
+ * @param values the members of this annotation instance
* @return the new mock Annotation Instance
*/
public static AnnotationInstance create(DotName name, AnnotationTarget target, List values) {
@@ -186,12 +186,12 @@ public static AnnotationInstance create(DotName name, AnnotationTarget target, L
}
/**
- * Construct a new mock annotation instance. The passed values list will be defensively copied.
+ * Construct a new mock annotation instance. The passed {@code values} list will be defensively copied.
*
* @param name the name of the annotation instance
* @param visible whether the annotation is visible at runtime via the reflection API
* @param target the thing the annotation is declared on
- * @param values the values of this annotation instance
+ * @param values the members of this annotation instance
* @return the new mock Annotation Instance
*/
public static AnnotationInstance create(DotName name, boolean visible, AnnotationTarget target,
@@ -222,10 +222,10 @@ public DotName name() {
}
/**
- * The Java element that this annotation was declared on. This can be
- * a class, a field, a method, or a method parameter. In addition it may
- * be null if this instance is a nested annotation, in which case there is
- * no target.
+ * The program element that this annotation was declared on. This can be
+ * a class, a field, a method, a method parameter, or a type in case of
+ * type annotations. In addition, it may be {@code null} if this instance
+ * is a nested annotation, in which case there is no target.
*
* @return the target this annotation instance refers to
*/
@@ -234,13 +234,13 @@ public AnnotationTarget target() {
}
/**
- * Returns a value that corresponds with the specified parameter name.
- * If the parameter was not specified by this instance then null is
- * returned. Note that this also applies to a defaulted parameter,
- * which is not recorded in the target class.
+ * Returns the member of this annotation that has the specified name.
+ * If the member was not specified by this instance, {@code null} is
+ * returned. Note that this also applies to a defaulted member,
+ * whose value is not recorded in the target class file.
*
- * @param name the parameter name
- * @return the value of the specified parameter, or null if not provided
+ * @param name the name of the annotation member
+ * @return the annotation member with specified name, or {@code null}
*/
public AnnotationValue value(final String name) {
int result = Arrays.binarySearch(values, name, new Comparator() {
@@ -252,34 +252,32 @@ public int compare(Object o1, Object o2) {
}
/**
- * Returns the value that is associated with the special default "value"
- * parameter.
+ * Returns the member that has the special default name {@code value}.
*
- * @return the "value" value
+ * @return the {@code value} member
*/
public AnnotationValue value() {
return value("value");
}
/**
- * Returns a value that corresponds with the specified parameter name,
+ * Returns the member of this annotation that has the specified name,
* accounting for its default value. Since an annotation's defaults are
* only stored on the annotation's defining class, and not usages of the
- * annotation, an index containing the Annotation class must be provided
+ * annotation, an index containing the annotation class must be provided
* as a parameter. If the index does not contain the defining annotation
- * class, then an IllegalArgumentException
will be thrown to
+ * class, then an {@code IllegalArgumentException} will be thrown to
* prevent non-deterministic results.
*
*
- * If the parameter was not specified by this instance, then the
- * annotation's ClassInfo
is checked for a default value.
- * If there is a default, that value is returned. Otherwise null is
- * returned.
+ * If the member was not specified by this annotation instance, then the
+ * annotation's {@code ClassInfo} is checked for a default value. If there
+ * is a default, that value is returned. Otherwise {@code null} is returned.
*
*
* @param index the index containing the defining annotation class
- * @param name the name of the annotation parameter
- * @return the value of the specified parameter, the default, or null
+ * @param name the name of the annotation member
+ * @return the annotation member with specified name, or {@code null}
* @throws IllegalArgumentException if index does not contain the defining
* annotation class
* @since 2.1
@@ -300,23 +298,22 @@ public AnnotationValue valueWithDefault(IndexView index, String name) {
}
/**
- * Returns the value that is associated with the special default "value"
- * parameter, also accounting for a value default. Since an annotation's
- * defaults are only stored on the annotation's defining class, and not
- * usages of the annotation, an index containing the Annotation class must
- * be provided as a parameter. If the index does not contain the defining
- * annotation class, then an IllegalArgumentException
will be
- * thrown to prevent non-deterministic results.
+ * Returns the member of this annotation that has special name {@code value},
+ * accounting for its default value. Since an annotation's defaults are only
+ * stored on the annotation's defining class, and not usages of the annotation,
+ * an index containing the annotation class must be provided as a parameter.
+ * If the index does not contain the defining annotation class, then an
+ * {@code IllegalArgumentException} will be thrown to prevent non-deterministic
+ * results.
*
*
- * If the "value" parameter was not specified by this instance, then the
- * annotation's ClassInfo
is checked for a default value.
- * If there is a default, that value is returned. Otherwise null is
- * returned.
+ * If the {@code value} member was not specified by this instance, then the
+ * annotation's {@code ClassInfo} is checked for a default value. If there
+ * is a default, that value is returned. Otherwise {@code null} is returned.
*
*
* @param index the index containing the defining annotation class
- * @return the "value" value, or its default, or null
+ * @return the {@code value} annotation member, or {@code null}
* @throws IllegalArgumentException if index does not contain the defining
* annotation class
* @since 2.1
@@ -326,19 +323,19 @@ public AnnotationValue valueWithDefault(IndexView index) {
}
/**
- * Returns a list of all parameter values on this annotation instance,
- * including default values id defined. Since an annotation's defaults are
- * only stored on the annotation's defining class, and not usages of the
- * annotation, an index containing the Annotation class must be provided as
- * a parameter. If the index does not contain the defining annotation class,
- * then an IllegalArgumentException
will be thrown to prevent
- * non-deterministic results.
+ * Returns a list of all members of this annotation instance, including default
+ * values if defined. Since an annotation's defaults are only stored on the
+ * annotation's defining class, and not usages of the annotation, an index
+ * containing the annotation class must be provided as a parameter. If the index
+ * does not contain the defining annotation class, then an
+ * {@code IllegalArgumentException} will be thrown to prevent non-deterministic
+ * results.
*
*
* The order of this list is undefined.
*
*
- * @return the parameter values of this annotation
+ * @return immutable list of this annotation's members
* @throws IllegalArgumentException if index does not contain the defining
* annotation class
* @since 2.1
@@ -365,15 +362,15 @@ public List valuesWithDefaults(IndexView index) {
}
/**
- * Returns a list of all parameter values on this annotation instance.
+ * Returns an immutable list of all members of this annotation instance.
* While random access is allowed, the ordering algorithm
* of the list should not be relied upon. Although it will
* be consistent for the life of this instance.
*
- * @return the parameter values of this annotation
+ * @return immutable list of this annotation's members
*/
public List values() {
- return Collections.unmodifiableList(Arrays.asList(values));
+ return new ImmutableArrayList<>(values);
}
AnnotationValue[] valueArray() {
diff --git a/core/src/main/java/org/jboss/jandex/ClassInfo.java b/core/src/main/java/org/jboss/jandex/ClassInfo.java
index 55657a5b..451396dd 100644
--- a/core/src/main/java/org/jboss/jandex/ClassInfo.java
+++ b/core/src/main/java/org/jboss/jandex/ClassInfo.java
@@ -62,18 +62,15 @@ public final class ClassInfo implements AnnotationTarget {
// Not final to allow lazy initialization, immutable once published
private short flags;
+ private boolean hasNoArgsConstructor;
private Type[] interfaceTypes;
private Type superClassType;
- private Type[] typeParameters;
private MethodInternal[] methods;
private FieldInternal[] fields;
- private RecordComponentInternal[] recordComponents;
private byte[] methodPositions = EMPTY_POSITIONS;
private byte[] fieldPositions = EMPTY_POSITIONS;
- private byte[] recordComponentPositions = EMPTY_POSITIONS;
- private boolean hasNoArgsConstructor;
private NestingInfo nestingInfo;
- private Set memberClasses;
+ private ExtraInfo extra;
/** Describes the form of nesting used by a class */
public enum NestingType {
@@ -92,16 +89,19 @@ public enum NestingType {
ANONYMOUS
}
+ // contains fields that are only seldom used, to make the `ClassInfo` class smaller
+ private static final class ExtraInfo {
+ private Type[] typeParameters;
+ private RecordComponentInternal[] recordComponents;
+ private byte[] recordComponentPositions = EMPTY_POSITIONS;
+ private Set memberClasses;
+ private ModuleInfo module;
+ }
+
private static final class NestingInfo {
private DotName enclosingClass;
private String simpleName;
private EnclosingMethodInfo enclosingMethod;
-
- // non-null if the class is in fact a module descriptor
- // this field would naturally belong to ClassInfo, but is present here
- // to make the ClassInfo object smaller in the most common case:
- // non-nested class that is not a module descriptor
- private ModuleInfo module;
}
/**
@@ -139,7 +139,7 @@ public Type returnType() {
* @return the list of parameters.
*/
public List parameters() {
- return Collections.unmodifiableList(Arrays.asList(parameters));
+ return new ImmutableArrayList<>(parameters);
}
Type[] parametersArray() {
@@ -186,7 +186,6 @@ public String toString() {
this.flags = flags;
this.interfaceTypes = interfaceTypes.length == 0 ? Type.EMPTY_ARRAY : interfaceTypes;
this.hasNoArgsConstructor = hasNoArgsConstructor;
- this.typeParameters = Type.EMPTY_ARRAY;
this.methods = MethodInternal.EMPTY_ARRAY;
this.fields = FieldInternal.EMPTY_ARRAY;
}
@@ -834,12 +833,16 @@ final byte[] fieldPositionArray() {
* @return the record component
*/
public final RecordComponentInfo recordComponent(String name) {
+ if (extra == null || extra.recordComponents == null) {
+ return null;
+ }
+
RecordComponentInternal key = new RecordComponentInternal(Utils.toUTF8(name), VoidType.VOID);
- int i = Arrays.binarySearch(recordComponents, key, RecordComponentInternal.NAME_COMPARATOR);
+ int i = Arrays.binarySearch(extra.recordComponents, key, RecordComponentInternal.NAME_COMPARATOR);
if (i < 0) {
return null;
}
- return new RecordComponentInfo(this, recordComponents[i]);
+ return new RecordComponentInfo(this, extra.recordComponents[i]);
}
/**
@@ -849,7 +852,11 @@ public final RecordComponentInfo recordComponent(String name) {
* @return a list of record components
*/
public final List recordComponents() {
- return new RecordComponentInfoGenerator(this, recordComponents, EMPTY_POSITIONS);
+ if (extra == null || extra.recordComponents == null) {
+ return Collections.emptyList();
+ }
+
+ return new RecordComponentInfoGenerator(this, extra.recordComponents, EMPTY_POSITIONS);
}
/**
@@ -866,15 +873,19 @@ public final List recordComponents() {
* @since 2.4
*/
public final List unsortedRecordComponents() {
- return new RecordComponentInfoGenerator(this, recordComponents, recordComponentPositions);
+ if (extra == null || extra.recordComponents == null) {
+ return Collections.emptyList();
+ }
+
+ return new RecordComponentInfoGenerator(this, extra.recordComponents, extra.recordComponentPositions);
}
final RecordComponentInternal[] recordComponentArray() {
- return recordComponents;
+ return extra != null && extra.recordComponents != null ? extra.recordComponents : RecordComponentInternal.EMPTY_ARRAY;
}
final byte[] recordComponentPositionArray() {
- return recordComponentPositions;
+ return extra != null && extra.recordComponentPositions != null ? extra.recordComponentPositions : EMPTY_POSITIONS;
}
/**
@@ -957,7 +968,7 @@ public int size() {
* @return immutable list of types declared in the {@code implements} clause of this class
*/
public final List interfaceTypes() {
- return Collections.unmodifiableList(Arrays.asList(interfaceTypes));
+ return new ImmutableArrayList<>(interfaceTypes);
}
final Type[] interfaceTypeArray() {
@@ -984,13 +995,20 @@ public final Type superClassType() {
* @return immutable list of generic type parameters of this class
*/
public final List typeParameters() {
- @SuppressWarnings({ "unchecked", "rawtypes" })
- List list = (List) Arrays.asList(typeParameters);
- return Collections.unmodifiableList(list);
+ if (extra == null || extra.typeParameters == null) {
+ return Collections.emptyList();
+ }
+
+ // type parameters are always `TypeVariable`
+ return new ImmutableArrayList(extra.typeParameters);
}
final Type[] typeParameterArray() {
- return typeParameters;
+ if (extra == null || extra.typeParameters == null) {
+ return Type.EMPTY_ARRAY;
+ }
+
+ return extra.typeParameters;
}
/**
@@ -1022,7 +1040,7 @@ public final boolean hasNoArgsConstructor() {
* @return the nesting type of this class
*/
public NestingType nestingType() {
- if (nestingInfo == null || nestingInfo.module != null) {
+ if (nestingInfo == null) {
return NestingType.TOP_LEVEL;
} else if (nestingInfo.enclosingClass != null) {
return NestingType.INNER;
@@ -1081,10 +1099,10 @@ public EnclosingMethodInfo enclosingMethod() {
* @return immutable set of names of this class's member classes, never {@code null}
*/
public Set memberClasses() {
- if (memberClasses == null) {
+ if (extra == null || extra.memberClasses == null) {
return Collections.emptySet();
}
- return Collections.unmodifiableSet(memberClasses);
+ return Collections.unmodifiableSet(extra.memberClasses);
}
/**
@@ -1093,7 +1111,7 @@ public Set memberClasses() {
* @return the module descriptor for module classes, otherwise {@code null}
*/
public ModuleInfo module() {
- return nestingInfo != null ? nestingInfo.module : null;
+ return extra != null ? extra.module : null;
}
@Override
@@ -1187,31 +1205,50 @@ void setMethods(List methods, NameTable names) {
}
void setRecordComponentArray(RecordComponentInternal[] recordComponents) {
- this.recordComponents = recordComponents;
+ if (recordComponents.length == 0) {
+ return;
+ }
+
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+
+ extra.recordComponents = recordComponents;
}
void setRecordComponentPositionArray(byte[] recordComponentPositions) {
- this.recordComponentPositions = recordComponentPositions;
+ if (recordComponentPositions.length == 0) {
+ return;
+ }
+
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+
+ extra.recordComponentPositions = recordComponentPositions;
}
void setRecordComponents(List recordComponents, NameTable names) {
final int size = recordComponents.size();
if (size == 0) {
- this.recordComponents = RecordComponentInternal.EMPTY_ARRAY;
return;
}
- this.recordComponents = new RecordComponentInternal[size];
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+
+ extra.recordComponents = new RecordComponentInternal[size];
for (int i = 0; i < size; i++) {
RecordComponentInfo recordComponentInfo = recordComponents.get(i);
RecordComponentInternal internal = names.intern(recordComponentInfo.recordComponentInternal());
recordComponentInfo.setRecordComponentInternal(internal);
- this.recordComponents[i] = internal;
+ extra.recordComponents[i] = internal;
}
- this.recordComponentPositions = sortAndGetPositions(this.recordComponents, RecordComponentInternal.NAME_COMPARATOR,
+ extra.recordComponentPositions = sortAndGetPositions(extra.recordComponents, RecordComponentInternal.NAME_COMPARATOR,
names);
}
@@ -1265,7 +1302,15 @@ void setInterfaceTypes(Type[] interfaceTypes) {
}
void setTypeParameters(Type[] typeParameters) {
- this.typeParameters = typeParameters.length == 0 ? Type.EMPTY_ARRAY : typeParameters;
+ if (typeParameters.length == 0) {
+ return;
+ }
+
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+
+ extra.typeParameters = typeParameters;
}
void setInnerClassInfo(DotName enclosingClass, String simpleName, boolean knownInnerClass) {
@@ -1286,7 +1331,15 @@ void setInnerClassInfo(DotName enclosingClass, String simpleName, boolean knownI
}
void setMemberClasses(Set memberClasses) {
- this.memberClasses = memberClasses;
+ if (memberClasses == null || memberClasses.isEmpty()) {
+ return;
+ }
+
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+
+ extra.memberClasses = memberClasses;
}
void setEnclosingMethod(EnclosingMethodInfo enclosingMethod) {
@@ -1306,11 +1359,11 @@ void setModule(ModuleInfo module) {
return;
}
- if (nestingInfo == null) {
- nestingInfo = new NestingInfo();
+ if (extra == null) {
+ extra = new ExtraInfo();
}
- nestingInfo.module = module;
+ extra.module = module;
}
void setFlags(short flags) {
diff --git a/core/src/main/java/org/jboss/jandex/FieldInternal.java b/core/src/main/java/org/jboss/jandex/FieldInternal.java
index b00ab8a5..e1162bd9 100644
--- a/core/src/main/java/org/jboss/jandex/FieldInternal.java
+++ b/core/src/main/java/org/jboss/jandex/FieldInternal.java
@@ -19,7 +19,6 @@
package org.jboss.jandex;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -151,7 +150,7 @@ final Type type() {
}
final List annotations() {
- return Collections.unmodifiableList(Arrays.asList(annotations));
+ return new ImmutableArrayList<>(annotations);
}
final AnnotationInstance[] annotationArray() {
diff --git a/core/src/main/java/org/jboss/jandex/ImmutableArrayList.java b/core/src/main/java/org/jboss/jandex/ImmutableArrayList.java
new file mode 100644
index 00000000..ffa60b4d
--- /dev/null
+++ b/core/src/main/java/org/jboss/jandex/ImmutableArrayList.java
@@ -0,0 +1,21 @@
+package org.jboss.jandex;
+
+import java.util.AbstractList;
+
+final class ImmutableArrayList extends AbstractList {
+ private final T[] array;
+
+ ImmutableArrayList(T[] array) {
+ this.array = array;
+ }
+
+ @Override
+ public T get(int index) {
+ return array[index];
+ }
+
+ @Override
+ public int size() {
+ return array.length;
+ }
+}
diff --git a/core/src/main/java/org/jboss/jandex/Index.java b/core/src/main/java/org/jboss/jandex/Index.java
index 578a315d..ba20c50b 100644
--- a/core/src/main/java/org/jboss/jandex/Index.java
+++ b/core/src/main/java/org/jboss/jandex/Index.java
@@ -18,6 +18,8 @@
package org.jboss.jandex;
+import static org.jboss.jandex.Utils.unfold;
+
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
@@ -67,28 +69,28 @@ public final class Index implements IndexView {
static final DotName REPEATABLE = DotName.createSimple("java.lang.annotation.Repeatable");
- final Map> annotations;
- final Map> subclasses;
- final Map> subinterfaces;
- final Map> implementors;
+ final Map annotations;
+ final Map subclasses;
+ final Map subinterfaces;
+ final Map implementors;
final Map classes;
final Map modules;
- final Map> users;
+ final Map users;
// populated lazily
volatile Map> classesInPackage;
volatile Map> subpackages;
- Index(Map> annotations, Map> subclasses,
- Map> subinterfaces, Map> implementors,
- Map classes, Map modules, Map> users) {
- this.annotations = Collections.unmodifiableMap(annotations);
- this.classes = Collections.unmodifiableMap(classes);
- this.subclasses = Collections.unmodifiableMap(subclasses);
- this.subinterfaces = Collections.unmodifiableMap(subinterfaces);
- this.implementors = Collections.unmodifiableMap(implementors);
- this.modules = Collections.unmodifiableMap(modules);
- this.users = Collections.unmodifiableMap(users);
+ Index(Map annotations, Map subclasses,
+ Map subinterfaces, Map implementors,
+ Map classes, Map modules, Map users) {
+ this.annotations = annotations;
+ this.classes = classes;
+ this.subclasses = subclasses;
+ this.subinterfaces = subinterfaces;
+ this.implementors = implementors;
+ this.modules = modules;
+ this.users = users;
}
/**
@@ -105,7 +107,12 @@ public final class Index implements IndexView {
*/
public static Index create(Map> annotations, Map> subclasses,
Map> implementors, Map classes) {
- return new Index(annotations, subclasses, Collections.emptyMap(), implementors, classes, Collections.emptyMap(),
+ return new Index(unfold(annotations, AnnotationInstance.class),
+ unfold(subclasses, ClassInfo.class),
+ Collections.emptyMap(),
+ unfold(implementors, ClassInfo.class),
+ classes,
+ Collections.emptyMap(),
Collections.emptyMap());
}
@@ -124,7 +131,13 @@ public static Index create(Map> annotations, M
*/
public static Index create(Map> annotations, Map> subclasses,
Map> implementors, Map classes, Map> users) {
- return new Index(annotations, subclasses, Collections.emptyMap(), implementors, classes, Collections.emptyMap(), users);
+ return new Index(unfold(annotations, AnnotationInstance.class),
+ unfold(subclasses, ClassInfo.class),
+ Collections.emptyMap(),
+ unfold(implementors, ClassInfo.class),
+ classes,
+ Collections.emptyMap(),
+ unfold(users, ClassInfo.class));
}
/**
@@ -144,7 +157,40 @@ public static Index create(Map> annotations, M
public static Index create(Map> annotations, Map> subclasses,
Map> subinterfaces, Map> implementors,
Map classes, Map> users) {
- return new Index(annotations, subclasses, subinterfaces, implementors, classes, Collections.emptyMap(), users);
+ return new Index(unfold(annotations, AnnotationInstance.class),
+ unfold(subclasses, ClassInfo.class),
+ unfold(subinterfaces, ClassInfo.class),
+ unfold(implementors, ClassInfo.class),
+ classes,
+ Collections.emptyMap(),
+ unfold(users, ClassInfo.class));
+ }
+
+ /**
+ * Constructs a "mock" Index using the passed values. All passed values MUST NOT BE MODIFIED AFTER THIS CALL.
+ * Otherwise the resulting object would not conform to the contract outlined above. Also, to conform to the
+ * memory efficiency contract this method should be passed componentized DotNames, which all share common root
+ * instances. Of course for testing code this doesn't really matter.
+ *
+ * @param annotations A map to lookup annotation instances by class name
+ * @param subclasses A map to lookup subclasses by super class name
+ * @param subinterfaces A map to lookup subinterfaces by super interface name
+ * @param implementors A map to lookup implementing classes by interface name
+ * @param classes A map to lookup classes by class name
+ * @param modules A map to lookup modules by name
+ * @param users A map to lookup class users
+ * @return the index
+ */
+ public static Index create(Map> annotations, Map> subclasses,
+ Map> subinterfaces, Map> implementors,
+ Map classes, Map modules, Map> users) {
+ return new Index(unfold(annotations, AnnotationInstance.class),
+ unfold(subclasses, ClassInfo.class),
+ unfold(subinterfaces, ClassInfo.class),
+ unfold(implementors, ClassInfo.class),
+ classes,
+ modules,
+ unfold(users, ClassInfo.class));
}
/**
@@ -269,8 +315,8 @@ public static ClassInfo singleClass(InputStream classData) throws IOException {
*/
@Override
public List getAnnotations(DotName annotationName) {
- List list = annotations.get(annotationName);
- return list == null ? EMPTY_ANNOTATION_LIST : Collections.unmodifiableList(list);
+ AnnotationInstance[] list = annotations.get(annotationName);
+ return list == null ? EMPTY_ANNOTATION_LIST : new ImmutableArrayList<>(list);
}
/**
@@ -311,8 +357,8 @@ private Collection getRepeatableAnnotations(DotName annotati
*/
@Override
public List getKnownDirectSubclasses(DotName className) {
- List list = subclasses.get(className);
- return list == null ? EMPTY_CLASSINFO_LIST : Collections.unmodifiableList(list);
+ ClassInfo[] list = subclasses.get(className);
+ return list == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(list);
}
@Override
@@ -354,8 +400,8 @@ private void getAllKnownSubClasses(DotName name, Set allKnown, Set getKnownDirectSubinterfaces(DotName interfaceName) {
- List list = subinterfaces.get(interfaceName);
- return list == null ? EMPTY_CLASSINFO_LIST : Collections.unmodifiableList(list);
+ ClassInfo[] list = subinterfaces.get(interfaceName);
+ return list == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(list);
}
/**
@@ -389,8 +435,8 @@ public Collection getAllKnownSubinterfaces(DotName interfaceName) {
*/
@Override
public List getKnownDirectImplementors(DotName className) {
- List list = implementors.get(className);
- return list == null ? EMPTY_CLASSINFO_LIST : Collections.unmodifiableList(list);
+ ClassInfo[] list = implementors.get(className);
+ return list == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(list);
}
/**
@@ -470,11 +516,8 @@ public ModuleInfo getModuleByName(DotName moduleName) {
*/
@Override
public List getKnownUsers(DotName className) {
- List ret = users.get(className);
- if (ret == null) {
- return EMPTY_CLASSINFO_LIST;
- }
- return Collections.unmodifiableList(ret);
+ ClassInfo[] ret = users.get(className);
+ return ret == null ? EMPTY_CLASSINFO_LIST : new ImmutableArrayList<>(ret);
}
/**
@@ -534,7 +577,7 @@ public Set getSubpackages(DotName packageName) {
*/
public void printAnnotations() {
System.out.println("Annotations:");
- for (Map.Entry> e : annotations.entrySet()) {
+ for (Map.Entry e : annotations.entrySet()) {
System.out.println(e.getKey() + ":");
for (AnnotationInstance instance : e.getValue()) {
AnnotationTarget target = instance.target();
@@ -571,7 +614,7 @@ public void printAnnotations() {
*/
public void printSubclasses() {
System.out.println("Subclasses:");
- for (Map.Entry> entry : subclasses.entrySet()) {
+ for (Map.Entry entry : subclasses.entrySet()) {
System.out.println(entry.getKey() + ":");
for (ClassInfo clazz : entry.getValue())
System.out.println(" " + clazz.name());
diff --git a/core/src/main/java/org/jboss/jandex/IndexReaderV2.java b/core/src/main/java/org/jboss/jandex/IndexReaderV2.java
index b0c0ff7a..2265340d 100644
--- a/core/src/main/java/org/jboss/jandex/IndexReaderV2.java
+++ b/core/src/main/java/org/jboss/jandex/IndexReaderV2.java
@@ -778,7 +778,7 @@ private List convertToList(AnnotationInstance[] annotationIn
return Collections.emptyList();
}
- return Collections.unmodifiableList(Arrays.asList(annotationInstances));
+ return new ImmutableArrayList<>(annotationInstances);
}
private void addClassToMap(HashMap> map, DotName name, ClassInfo currentClass) {
@@ -903,7 +903,7 @@ private Index readClasses(PackedDataInputStream stream,
Map modules = (version >= 10) ? readModules(stream, masterAnnotations)
: Collections. emptyMap();
- return new Index(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users);
+ return Index.create(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users);
}
private Map readModules(PackedDataInputStream stream,
diff --git a/core/src/main/java/org/jboss/jandex/IndexWriterV2.java b/core/src/main/java/org/jboss/jandex/IndexWriterV2.java
index ead26ff4..debb06f8 100644
--- a/core/src/main/java/org/jboss/jandex/IndexWriterV2.java
+++ b/core/src/main/java/org/jboss/jandex/IndexWriterV2.java
@@ -226,15 +226,15 @@ int write(Index index) throws IOException {
return stream.size();
}
- private void writeUsersTable(PackedDataOutputStream stream, Map> users) throws IOException {
- for (Entry> entry : users.entrySet()) {
+ private void writeUsersTable(PackedDataOutputStream stream, Map users) throws IOException {
+ for (Entry entry : users.entrySet()) {
writeUsersSet(stream, entry.getKey(), entry.getValue());
}
}
- private void writeUsersSet(PackedDataOutputStream stream, DotName user, List uses) throws IOException {
+ private void writeUsersSet(PackedDataOutputStream stream, DotName user, ClassInfo[] uses) throws IOException {
stream.writePackedU32(positionOf(user));
- stream.writePackedU32(uses.size());
+ stream.writePackedU32(uses.length);
for (ClassInfo use : uses) {
stream.writePackedU32(positionOf(use.name()));
}
@@ -895,7 +895,7 @@ private void buildTables(Index index) {
}
if (index.users != null) {
- for (Entry> entry : index.users.entrySet()) {
+ for (Entry entry : index.users.entrySet()) {
addClassName(entry.getKey());
for (ClassInfo classInfo : entry.getValue()) {
addClassName(classInfo.name());
diff --git a/core/src/main/java/org/jboss/jandex/Indexer.java b/core/src/main/java/org/jboss/jandex/Indexer.java
index 1463adea..80a492fc 100644
--- a/core/src/main/java/org/jboss/jandex/Indexer.java
+++ b/core/src/main/java/org/jboss/jandex/Indexer.java
@@ -2403,7 +2403,7 @@ public Index complete() {
propagateTypeVariables();
try {
- return new Index(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users);
+ return Index.create(masterAnnotations, subclasses, subinterfaces, implementors, classes, modules, users);
} finally {
masterAnnotations = null;
subclasses = null;
diff --git a/core/src/main/java/org/jboss/jandex/MethodInternal.java b/core/src/main/java/org/jboss/jandex/MethodInternal.java
index 6521249d..59ab5d5f 100644
--- a/core/src/main/java/org/jboss/jandex/MethodInternal.java
+++ b/core/src/main/java/org/jboss/jandex/MethodInternal.java
@@ -32,7 +32,7 @@ final class MethodInternal implements Interned {
static final NameAndParameterComponentComparator NAME_AND_PARAMETER_COMPONENT_COMPARATOR = new NameAndParameterComponentComparator();
static final byte[][] EMPTY_PARAMETER_NAMES = new byte[0][];
- static class NameAndParameterComponentComparator implements Comparator {
+ private static final class NameAndParameterComponentComparator implements Comparator {
private int compare(byte[] left, byte[] right) {
for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) {
int a = (left[i] & 0xff);
@@ -72,16 +72,21 @@ public int compare(MethodInternal instance, MethodInternal instance2) {
}
}
+ // contains fields that are only seldom used, to make the `MethodInternal` class smaller
+ private static final class ExtraInfo {
+ Type receiverType;
+ Type[] typeParameters;
+ AnnotationValue defaultValue;
+ AnnotationInstance[] annotations;
+ }
+
private byte[] name;
private byte[][] parameterNames;
private Type[] parameterTypes;
private Type returnType;
private Type[] exceptions;
- private Type receiverType;
- private Type[] typeParameters;
- private AnnotationInstance[] annotations;
- private AnnotationValue defaultValue;
private short flags;
+ private ExtraInfo extra;
private final Type[] descriptorParameterTypes;
@@ -103,11 +108,31 @@ public int compare(MethodInternal instance, MethodInternal instance2) {
this.parameterTypes = parameterTypes.length == 0 ? Type.EMPTY_ARRAY : parameterTypes;
this.returnType = returnType;
this.flags = flags;
- this.annotations = annotations;
this.exceptions = exceptions;
- this.typeParameters = typeParameters;
- this.receiverType = receiverType;
- this.defaultValue = defaultValue;
+ if (annotations != null && annotations.length > 0) {
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.annotations = annotations;
+ }
+ if (typeParameters != null && typeParameters.length > 0) {
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.typeParameters = typeParameters;
+ }
+ if (receiverType != null) {
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.receiverType = receiverType;
+ }
+ if (defaultValue != null) {
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.defaultValue = defaultValue;
+ }
this.descriptorParameterTypes = this.parameterTypes;
}
@@ -123,10 +148,14 @@ public boolean equals(Object o) {
MethodInternal methodInternal = (MethodInternal) o;
+ if (extra != null && methodInternal.extra == null || extra == null && methodInternal.extra != null) {
+ return false;
+ }
+
if (flags != methodInternal.flags) {
return false;
}
- if (!Arrays.equals(annotations, methodInternal.annotations)) {
+ if (extra != null && !Arrays.equals(extra.annotations, methodInternal.extra.annotations)) {
return false;
}
if (!Arrays.equals(exceptions, methodInternal.exceptions)) {
@@ -144,16 +173,21 @@ public boolean equals(Object o) {
if (!Arrays.equals(descriptorParameterTypes, methodInternal.descriptorParameterTypes)) {
return false;
}
- if (receiverType != null ? !receiverType.equals(methodInternal.receiverType) : methodInternal.receiverType != null) {
+ if (extra != null && (extra.receiverType != null ? !extra.receiverType.equals(methodInternal.extra.receiverType)
+ : methodInternal.extra.receiverType != null)) {
return false;
}
if (!returnType.equals(methodInternal.returnType)) {
return false;
}
- if (defaultValue != null ? !defaultValue.equals(methodInternal.defaultValue) : methodInternal.defaultValue != null) {
+ if (extra != null && (extra.defaultValue != null ? !extra.defaultValue.equals(methodInternal.extra.defaultValue)
+ : methodInternal.extra.defaultValue != null)) {
return false;
}
- return Arrays.equals(typeParameters, methodInternal.typeParameters);
+ if (extra != null && !Arrays.equals(extra.typeParameters, methodInternal.extra.typeParameters)) {
+ return false;
+ }
+ return true;
}
@Override
@@ -164,11 +198,11 @@ public int hashCode() {
result = 31 * result + Arrays.hashCode(descriptorParameterTypes);
result = 31 * result + returnType.hashCode();
result = 31 * result + Arrays.hashCode(exceptions);
- result = 31 * result + (receiverType != null ? receiverType.hashCode() : 0);
- result = 31 * result + Arrays.hashCode(typeParameters);
- result = 31 * result + Arrays.hashCode(annotations);
+ result = 31 * result + (extra != null && extra.receiverType != null ? extra.receiverType.hashCode() : 0);
+ result = 31 * result + (extra != null ? Arrays.hashCode(extra.typeParameters) : 0);
+ result = 31 * result + (extra != null ? Arrays.hashCode(extra.annotations) : 0);
+ result = 31 * result + (extra != null && extra.defaultValue != null ? extra.defaultValue.hashCode() : 0);
result = 31 * result + (int) flags;
- result = 31 * result + (defaultValue != null ? defaultValue.hashCode() : 0);
return result;
}
@@ -183,10 +217,14 @@ public boolean internEquals(Object o) {
MethodInternal methodInternal = (MethodInternal) o;
+ if (extra != null && methodInternal.extra == null || extra == null && methodInternal.extra != null) {
+ return false;
+ }
+
if (flags != methodInternal.flags) {
return false;
}
- if (!Arrays.equals(annotations, methodInternal.annotations)) {
+ if (extra != null && !Arrays.equals(extra.annotations, methodInternal.extra.annotations)) {
return false;
}
if (!Interned.arrayEquals(exceptions, methodInternal.exceptions)) {
@@ -204,17 +242,21 @@ public boolean internEquals(Object o) {
if (!Interned.arrayEquals(descriptorParameterTypes, methodInternal.descriptorParameterTypes)) {
return false;
}
- if (receiverType != null ? !receiverType.internEquals(methodInternal.receiverType)
- : methodInternal.receiverType != null) {
+ if (extra != null && (extra.receiverType != null ? !extra.receiverType.internEquals(methodInternal.extra.receiverType)
+ : methodInternal.extra.receiverType != null)) {
return false;
}
if (!returnType.internEquals(methodInternal.returnType)) {
return false;
}
- if (defaultValue != null ? !defaultValue.equals(methodInternal.defaultValue) : methodInternal.defaultValue != null) {
+ if (extra != null && (extra.defaultValue != null ? !extra.defaultValue.equals(methodInternal.extra.defaultValue)
+ : methodInternal.extra.defaultValue != null)) {
+ return false;
+ }
+ if (extra != null && !Interned.arrayEquals(extra.typeParameters, methodInternal.extra.typeParameters)) {
return false;
}
- return Interned.arrayEquals(typeParameters, methodInternal.typeParameters);
+ return true;
}
@Override
@@ -225,11 +267,11 @@ public int internHashCode() {
result = 31 * result + Interned.arrayHashCode(descriptorParameterTypes);
result = 31 * result + returnType.internHashCode();
result = 31 * result + Interned.arrayHashCode(exceptions);
- result = 31 * result + (receiverType != null ? receiverType.internHashCode() : 0);
- result = 31 * result + Interned.arrayHashCode(typeParameters);
- result = 31 * result + Arrays.hashCode(annotations);
+ result = 31 * result + (extra != null && extra.receiverType != null ? extra.receiverType.internHashCode() : 0);
+ result = 31 * result + (extra != null ? Interned.arrayHashCode(extra.typeParameters) : 0);
+ result = 31 * result + (extra != null ? Arrays.hashCode(extra.annotations) : 0);
+ result = 31 * result + (extra != null && extra.defaultValue != null ? extra.defaultValue.hashCode() : 0);
result = 31 * result + (int) flags;
- result = 31 * result + (defaultValue != null ? defaultValue.hashCode() : 0);
return result;
}
@@ -268,11 +310,11 @@ final Type[] copyExceptions() {
}
final List parameterTypes() {
- return Collections.unmodifiableList(Arrays.asList(parameterTypes));
+ return new ImmutableArrayList<>(parameterTypes);
}
final List descriptorParameterTypes() {
- return Collections.unmodifiableList(Arrays.asList(descriptorParameterTypes));
+ return new ImmutableArrayList<>(descriptorParameterTypes);
}
final Type[] descriptorParameterTypesArray() {
@@ -284,8 +326,8 @@ final Type returnType() {
}
final Type receiverType(ClassInfo clazz) {
- if (receiverType != null) {
- return receiverType;
+ if (extra != null && extra.receiverType != null) {
+ return extra.receiverType;
}
// don't assign the `receiverType` field here! receiver type declaration is only useful
@@ -302,11 +344,11 @@ final Type receiverType(ClassInfo clazz) {
}
final Type receiverTypeField() {
- return receiverType;
+ return extra != null ? extra.receiverType : null;
}
final List exceptions() {
- return Collections.unmodifiableList(Arrays.asList(exceptions));
+ return new ImmutableArrayList<>(exceptions);
}
final Type[] exceptionArray() {
@@ -314,21 +356,22 @@ final Type[] exceptionArray() {
}
final List typeParameters() {
- @SuppressWarnings("unchecked") // type parameters will always be TypeVariable[]
- List list = (List) Arrays.asList(typeParameters);
- return Collections.unmodifiableList(list);
+ // type parameters are always `TypeVariable`
+ return extra != null && extra.typeParameters != null ? new ImmutableArrayList(extra.typeParameters)
+ : Collections.emptyList();
}
final List annotations() {
- return Collections.unmodifiableList(Arrays.asList(annotations));
+ return extra != null && extra.annotations != null ? new ImmutableArrayList<>(extra.annotations)
+ : Collections.emptyList();
}
final AnnotationInstance[] annotationArray() {
- return annotations;
+ return extra != null && extra.annotations != null ? extra.annotations : AnnotationInstance.EMPTY_ARRAY;
}
final AnnotationInstance annotation(DotName name) {
- return AnnotationInstance.binarySearch(annotations, name);
+ return extra != null && extra.annotations != null ? AnnotationInstance.binarySearch(extra.annotations, name) : null;
}
final boolean hasAnnotation(DotName name) {
@@ -336,11 +379,11 @@ final boolean hasAnnotation(DotName name) {
}
final Type[] typeParameterArray() {
- return typeParameters;
+ return extra != null && extra.typeParameters != null ? extra.typeParameters : Type.EMPTY_ARRAY;
}
final AnnotationValue defaultValue() {
- return defaultValue;
+ return extra != null ? extra.defaultValue : null;
}
final short flags() {
@@ -352,8 +395,8 @@ public String toString() {
StringBuilder builder = new StringBuilder();
String name = name();
builder.append(returnType.toString(true)).append(' ').append(name).append('(');
- if (receiverType != null) {
- builder.append(receiverType.toString(true)).append(" this");
+ if (extra != null && extra.receiverType != null) {
+ builder.append(extra.receiverType.toString(true)).append(" this");
if (parameterTypes.length > 0) {
builder.append(", ");
}
@@ -385,7 +428,10 @@ public String toString() {
void setTypeParameters(Type[] typeParameters) {
if (typeParameters.length > 0) {
- this.typeParameters = typeParameters;
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.typeParameters = typeParameters;
}
}
@@ -406,17 +452,26 @@ void setExceptions(Type[] exceptions) {
}
void setReceiverType(Type receiverType) {
- this.receiverType = receiverType;
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.receiverType = receiverType;
}
void setAnnotations(List annotations) {
- if (annotations.size() > 0) {
- this.annotations = annotations.toArray(new AnnotationInstance[annotations.size()]);
- Arrays.sort(this.annotations, AnnotationInstance.NAME_COMPARATOR);
+ if (!annotations.isEmpty()) {
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.annotations = annotations.toArray(new AnnotationInstance[0]);
+ Arrays.sort(extra.annotations, AnnotationInstance.NAME_COMPARATOR);
}
}
void setDefaultValue(AnnotationValue defaultValue) {
- this.defaultValue = defaultValue;
+ if (extra == null) {
+ extra = new ExtraInfo();
+ }
+ extra.defaultValue = defaultValue;
}
}
diff --git a/core/src/main/java/org/jboss/jandex/ParameterizedType.java b/core/src/main/java/org/jboss/jandex/ParameterizedType.java
index 905d8551..ac315b07 100644
--- a/core/src/main/java/org/jboss/jandex/ParameterizedType.java
+++ b/core/src/main/java/org/jboss/jandex/ParameterizedType.java
@@ -18,7 +18,6 @@
package org.jboss.jandex;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
/**
@@ -99,7 +98,7 @@ public static ParameterizedType create(DotName name, Type[] arguments, Type owne
* @return the list of type arguments, or empty if none
*/
public List arguments() {
- return Collections.unmodifiableList(Arrays.asList(arguments));
+ return new ImmutableArrayList<>(arguments);
}
Type[] argumentsArray() {
diff --git a/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java b/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java
index 034ea7d2..7656b060 100644
--- a/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java
+++ b/core/src/main/java/org/jboss/jandex/RecordComponentInternal.java
@@ -19,7 +19,6 @@
package org.jboss.jandex;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -138,7 +137,7 @@ final Type type() {
}
final List annotations() {
- return Collections.unmodifiableList(Arrays.asList(annotations));
+ return new ImmutableArrayList<>(annotations);
}
final AnnotationInstance[] annotationArray() {
diff --git a/core/src/main/java/org/jboss/jandex/Result.java b/core/src/main/java/org/jboss/jandex/Result.java
index 75d94a92..c53b37a9 100644
--- a/core/src/main/java/org/jboss/jandex/Result.java
+++ b/core/src/main/java/org/jboss/jandex/Result.java
@@ -19,7 +19,6 @@
package org.jboss.jandex;
import java.io.File;
-import java.util.List;
/**
* The result from a jar indexing operation.
@@ -44,8 +43,8 @@ public final class Result {
instances = countInstances(index);
classes = index.classes.size();
int usages = 0;
- for (List usagesForOneClass : index.users.values()) {
- usages += usagesForOneClass.size();
+ for (ClassInfo[] usagesForOneClass : index.users.values()) {
+ usages += usagesForOneClass.length;
}
this.usages = usages;
this.bytes = bytes;
@@ -55,8 +54,8 @@ public final class Result {
private int countInstances(Index index) {
int c = 0;
- for (List list : index.annotations.values())
- c += list.size();
+ for (AnnotationInstance[] list : index.annotations.values())
+ c += list.length;
return c;
}
diff --git a/core/src/main/java/org/jboss/jandex/Type.java b/core/src/main/java/org/jboss/jandex/Type.java
index 7d079cef..852e2f45 100644
--- a/core/src/main/java/org/jboss/jandex/Type.java
+++ b/core/src/main/java/org/jboss/jandex/Type.java
@@ -429,7 +429,7 @@ public final List annotationsWithRepeatable(DotName name, In
* @since 2.0
*/
public List annotations() {
- return Collections.unmodifiableList(Arrays.asList(annotations));
+ return new ImmutableArrayList<>(annotations);
}
Type addAnnotation(AnnotationInstance annotation) {
diff --git a/core/src/main/java/org/jboss/jandex/TypeVariable.java b/core/src/main/java/org/jboss/jandex/TypeVariable.java
index 94064a5c..3a4899a9 100644
--- a/core/src/main/java/org/jboss/jandex/TypeVariable.java
+++ b/core/src/main/java/org/jboss/jandex/TypeVariable.java
@@ -18,7 +18,6 @@
package org.jboss.jandex;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
/**
@@ -95,7 +94,7 @@ public String identifier() {
}
public List bounds() {
- return Collections.unmodifiableList(Arrays.asList(bounds));
+ return new ImmutableArrayList<>(bounds);
}
Type[] boundArray() {
diff --git a/core/src/main/java/org/jboss/jandex/Utils.java b/core/src/main/java/org/jboss/jandex/Utils.java
index d0c32236..c6f8a667 100644
--- a/core/src/main/java/org/jboss/jandex/Utils.java
+++ b/core/src/main/java/org/jboss/jandex/Utils.java
@@ -18,10 +18,11 @@
package org.jboss.jandex;
+import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -41,16 +42,17 @@ static String fromUTF8(byte[] bytes) {
return new String(bytes, StandardCharsets.UTF_8);
}
- static List emptyOrWrap(List list) {
- return list.size() == 0 ? Collections. emptyList() : Collections.unmodifiableList(list);
- }
-
- static Collection emptyOrWrap(Collection list) {
- return list.size() == 0 ? Collections. emptyList() : Collections.unmodifiableCollection(list);
- }
-
- static Map emptyOrWrap(Map map) {
- return map.size() == 0 ? Collections. emptyMap() : Collections.unmodifiableMap(map);
+ static Map unfold(Map> map, Class listElementType) {
+ if (map.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ Map result = new HashMap<>();
+ map.forEach((key, value) -> {
+ V[] array = (V[]) Array.newInstance(listElementType, value.size());
+ result.put(key, value.toArray(array));
+ });
+ return result;
}
static List listOfCapacity(int capacity) {