Skip to content

Commit

Permalink
Reduce allocations when creating immutable array-backed lists
Browse files Browse the repository at this point in the history
This commit replaces `Collections.unmodifiableList(Arrays.asList(array))`
with `new ImmutableArrayList<>(array)`, where `ImmutableArrayList` is
a simple implementation of an immutable array-backed list. This reduces
the number of allocations from 2 to 1 on many places in the code.

Additionally, this commit slightly improves javadoc of some methods
in the `AnnotationInstance` class.
  • Loading branch information
Ladicek committed Mar 27, 2024
1 parent 07b9a4d commit f42f4a6
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 66 deletions.
93 changes: 45 additions & 48 deletions src/main/java/org/jboss/jandex/AnnotationInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,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).
*
* <p>
Expand Down Expand Up @@ -70,7 +70,7 @@ public int compare(AnnotationInstance instance, AnnotationInstance instance2) {
*
* @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 final AnnotationInstance create(DotName name, AnnotationTarget target, AnnotationValue[] values) {
Expand All @@ -97,7 +97,7 @@ public int compare(AnnotationValue o1, AnnotationValue o2) {
*
* @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 final AnnotationInstance create(DotName name, AnnotationTarget target, List<AnnotationValue> values) {
Expand All @@ -120,10 +120,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
*/
Expand All @@ -132,13 +132,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<Object>() {
Expand All @@ -150,35 +150,33 @@ 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 <code>IllegalArgumentException</code> will be thrown to
* class, then an {@code IllegalArgumentException} will be thrown to
* prevent non-deterministic results.
*
* <p>
* If the parameter was not specified by this instance, then the
* annotation's <code>ClassInfo</code> 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.
* </p>
*
* @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
Expand All @@ -199,23 +197,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 <code>IllegalArgumentException</code> 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.
*
* <p>
* If the "value" parameter was not specified by this instance, then the
* annotation's <code>ClassInfo</code> 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.
* </p>
*
* @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
Expand All @@ -226,17 +223,17 @@ 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 <code>IllegalArgumentException</code> 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.
*
* <p>The order of this list is undefined.</p>
*
* @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
Expand All @@ -263,15 +260,15 @@ public List<AnnotationValue> 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<AnnotationValue> values() {
return Collections.unmodifiableList(Arrays.asList(values));
return new ImmutableArrayList<AnnotationValue>(values);
}

AnnotationValue[] valueArray() {
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/org/jboss/jandex/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public Type returnType() {
* @return the list of parameters.
*/
public List<Type> parameters() {
return Collections.unmodifiableList(Arrays.asList(parameters));
return new ImmutableArrayList<Type>(parameters);
}

Type[] parametersArray() {
Expand Down Expand Up @@ -597,7 +597,7 @@ public int size() {
* @return the list of types declared in the implements clause of this class
*/
public final List<Type> interfaceTypes() {
return Collections.unmodifiableList(Arrays.asList(interfaceTypes));
return new ImmutableArrayList<Type>(interfaceTypes);
}

final Type[] interfaceTypeArray() {
Expand All @@ -624,9 +624,8 @@ public final Type superClassType() {
* @return the generic type parameters of this class
*/
public final List<TypeVariable> typeParameters() {
@SuppressWarnings({ "unchecked", "rawtypes" })
List<TypeVariable> list = (List) Arrays.asList(typeParameters);
return Collections.unmodifiableList(list);
// type parameters are always `TypeVariable`
return new ImmutableArrayList(typeParameters);
}

final Type[] typeParameterArray() {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jboss/jandex/FieldInternal.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ final Type type() {
}

final List<AnnotationInstance> annotations() {
return Collections.unmodifiableList(Arrays.asList(annotations));
return new ImmutableArrayList<AnnotationInstance>(annotations);
}

final AnnotationInstance[] annotationArray() {
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/org/jboss/jandex/ImmutableArrayList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.jboss.jandex;

import java.util.AbstractList;

final class ImmutableArrayList<T> extends AbstractList<T> {
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;
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/jboss/jandex/IndexReaderV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ private List<AnnotationInstance> convertToList(AnnotationInstance[] annotationIn
return Collections.emptyList();
}

return Collections.unmodifiableList(Arrays.asList(annotationInstances));
return new ImmutableArrayList<AnnotationInstance>(annotationInstances);
}

private void addClassToMap(HashMap<DotName, List<ClassInfo>> map, DotName name, ClassInfo currentClass) {
Expand Down
13 changes: 6 additions & 7 deletions src/main/java/org/jboss/jandex/MethodInternal.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ final Type[] copyExceptions() {
}

final List<Type> parameters() {
return Collections.unmodifiableList(Arrays.asList(parameters));
return new ImmutableArrayList<Type>(parameters);
}

final Type returnType() {
Expand All @@ -211,21 +211,20 @@ final Type receiverTypeField() {
}

final List<Type> exceptions() {
return Collections.unmodifiableList(Arrays.asList(exceptions));
return new ImmutableArrayList<Type>(exceptions);
}

final Type[] exceptionArray() {
return exceptions;
}

final List<TypeVariable> typeParameters() {
@SuppressWarnings("unchecked") // type parameters will always be TypeVariable[]
List<TypeVariable> list = (List) Arrays.asList(typeParameters);
return Collections.unmodifiableList(list);
// type parameters are always `TypeVariable`
return new ImmutableArrayList(typeParameters);
}

final List<AnnotationInstance> annotations() {
return Collections.unmodifiableList(Arrays.asList(annotations));
return new ImmutableArrayList<AnnotationInstance>(annotations);
}

final AnnotationInstance[] annotationArray() {
Expand Down Expand Up @@ -293,7 +292,7 @@ void setTypeParameters(Type[] typeParameters) {
void setParameterNames(byte[][] parameterNames) {
this.parameterNames = parameterNames;
}

void setParameters(Type[] parameters) {
this.parameters = parameters.length == 0 ? Type.EMPTY_ARRAY : parameters;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jboss/jandex/ParameterizedType.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public static ParameterizedType create(DotName name, Type[] arguments, Type owne
* @return the list of type arguments, or empty if none
*/
public List<Type> arguments() {
return Collections.unmodifiableList(Arrays.asList(arguments));
return new ImmutableArrayList<Type>(arguments);
}

Type[] argumentsArray() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ final Type type() {
}

final List<AnnotationInstance> annotations() {
return Collections.unmodifiableList(Arrays.asList(annotations));
return new ImmutableArrayList<AnnotationInstance>(annotations);
}

final AnnotationInstance[] annotationArray() {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jboss/jandex/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ public boolean equals(Object o) {
* @since 2.0
*/
public List<AnnotationInstance> annotations() {
return Collections.unmodifiableList(Arrays.asList(annotations));
return new ImmutableArrayList<AnnotationInstance>(annotations);
}

AnnotationInstance[] annotationArray() {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jboss/jandex/TypeVariable.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public String identifier() {
}

public List<Type> bounds() {
return Collections.unmodifiableList(Arrays.asList(bounds));
return new ImmutableArrayList<Type>(bounds);
}

Type[] boundArray() {
Expand Down

0 comments on commit f42f4a6

Please sign in to comment.