diff --git a/core/src/main/java/org/apache/iceberg/DeleteFileIndex.java b/core/src/main/java/org/apache/iceberg/DeleteFileIndex.java index edef7c322896..f8fab0930585 100644 --- a/core/src/main/java/org/apache/iceberg/DeleteFileIndex.java +++ b/core/src/main/java/org/apache/iceberg/DeleteFileIndex.java @@ -396,12 +396,14 @@ DeleteFileIndex build() { }); // build a map from (specId, partition) to delete file entries + Map wrappersBySpecId = Maps.newHashMap(); ListMultimap, ManifestEntry> deleteFilesByPartition = Multimaps.newListMultimap(Maps.newHashMap(), Lists::newArrayList); for (ManifestEntry entry : deleteEntries) { int specId = entry.file().specId(); - StructLikeWrapper wrapper = StructLikeWrapper.forType(specsById.get(specId).partitionType()) - .set(entry.file().partition()); + StructLikeWrapper wrapper = wrappersBySpecId + .computeIfAbsent(specId, id -> StructLikeWrapper.forType(specsById.get(id).partitionType())) + .copyFor(entry.file().partition()); deleteFilesByPartition.put(Pair.of(specId, wrapper), entry); } diff --git a/core/src/main/java/org/apache/iceberg/actions/BaseRewriteDataFilesAction.java b/core/src/main/java/org/apache/iceberg/actions/BaseRewriteDataFilesAction.java index bf9aa5b37fad..1cdee7224650 100644 --- a/core/src/main/java/org/apache/iceberg/actions/BaseRewriteDataFilesAction.java +++ b/core/src/main/java/org/apache/iceberg/actions/BaseRewriteDataFilesAction.java @@ -259,9 +259,10 @@ private Map> groupTasksByPartition( CloseableIterator tasksIter) { ListMultimap tasksGroupedByPartition = Multimaps.newListMultimap( Maps.newHashMap(), Lists::newArrayList); + StructLikeWrapper partitionWrapper = StructLikeWrapper.forType(spec.partitionType()); try (CloseableIterator iterator = tasksIter) { iterator.forEachRemaining(task -> { - StructLikeWrapper structLike = StructLikeWrapper.forType(spec.partitionType()).set(task.file().partition()); + StructLikeWrapper structLike = partitionWrapper.copyFor(task.file().partition()); tasksGroupedByPartition.put(structLike, task); }); } catch (IOException e) { diff --git a/core/src/main/java/org/apache/iceberg/util/StructLikeMap.java b/core/src/main/java/org/apache/iceberg/util/StructLikeMap.java index dc6e6ab9acb0..66141fb0bcd4 100644 --- a/core/src/main/java/org/apache/iceberg/util/StructLikeMap.java +++ b/core/src/main/java/org/apache/iceberg/util/StructLikeMap.java @@ -84,7 +84,7 @@ public T get(Object key) { @Override public T put(StructLike key, T value) { - return wrapperMap.put(StructLikeWrapper.forType(type).set(key), value); + return wrapperMap.put(wrappers.get().copyFor(key), value); } @Override diff --git a/core/src/main/java/org/apache/iceberg/util/StructLikeSet.java b/core/src/main/java/org/apache/iceberg/util/StructLikeSet.java index 71454b39eb20..8e76490c3f4e 100644 --- a/core/src/main/java/org/apache/iceberg/util/StructLikeSet.java +++ b/core/src/main/java/org/apache/iceberg/util/StructLikeSet.java @@ -100,7 +100,7 @@ public T[] toArray(T[] destArray) { @Override public boolean add(StructLike struct) { - return wrapperSet.add(StructLikeWrapper.forType(type).set(struct)); + return wrapperSet.add(wrappers.get().copyFor(struct)); } @Override @@ -126,7 +126,7 @@ public boolean containsAll(Collection objects) { public boolean addAll(Collection structs) { if (structs != null) { return Iterables.addAll(wrapperSet, - Iterables.transform(structs, struct -> StructLikeWrapper.forType(type).set(struct))); + Iterables.transform(structs, struct -> wrappers.get().copyFor(struct))); } return false; } diff --git a/core/src/main/java/org/apache/iceberg/util/StructLikeWrapper.java b/core/src/main/java/org/apache/iceberg/util/StructLikeWrapper.java index e108e8e78085..5d614acac566 100644 --- a/core/src/main/java/org/apache/iceberg/util/StructLikeWrapper.java +++ b/core/src/main/java/org/apache/iceberg/util/StructLikeWrapper.java @@ -40,11 +40,28 @@ public static StructLikeWrapper forType(Types.StructType struct) { private StructLike struct; private StructLikeWrapper(Types.StructType type) { - this.comparator = Comparators.forType(type); - this.structHash = JavaHash.forType(type); + this(Comparators.forType(type), JavaHash.forType(type)); + } + + private StructLikeWrapper(Comparator comparator, JavaHash structHash) { + this.comparator = comparator; + this.structHash = structHash; this.hashCode = null; } + /** + * Creates a copy of this wrapper that wraps a struct. + *

+ * This is equivalent to {@code new StructLikeWrapper(type).set(newStruct)} but is cheaper because no analysis of the + * type is necessary. + * + * @param newStruct a {@link StructLike} row + * @return a copy of this wrapper wrapping the give struct + */ + public StructLikeWrapper copyFor(StructLike newStruct) { + return new StructLikeWrapper(comparator, structHash).set(newStruct); + } + public StructLikeWrapper set(StructLike newStruct) { this.struct = newStruct; this.hashCode = null; diff --git a/data/src/main/java/org/apache/iceberg/data/DeleteFilter.java b/data/src/main/java/org/apache/iceberg/data/DeleteFilter.java index 3bb44a1e4e0f..252f9f179ca5 100644 --- a/data/src/main/java/org/apache/iceberg/data/DeleteFilter.java +++ b/data/src/main/java/org/apache/iceberg/data/DeleteFilter.java @@ -147,6 +147,7 @@ private List> applyEqDeletes() { Iterable deletes = entry.getValue(); Schema deleteSchema = TypeUtil.select(requiredSchema, ids); + InternalRecordWrapper wrapper = new InternalRecordWrapper(deleteSchema.asStruct()); // a projection to select and reorder fields of the file schema to match the delete rows StructProjection projectRow = StructProjection.create(requiredSchema, deleteSchema); @@ -159,9 +160,7 @@ private List> applyEqDeletes() { CloseableIterable.concat(deleteRecords), Record::copy); StructLikeSet deleteSet = Deletes.toEqualitySet( - CloseableIterable.transform( - records, record -> new InternalRecordWrapper(deleteSchema.asStruct()).wrap(record)), - deleteSchema.asStruct()); + CloseableIterable.transform(records, wrapper::copyFor), deleteSchema.asStruct()); Predicate isInDeleteSet = record -> deleteSet.contains(projectRow.wrap(asStructLike(record))); isInDeleteSets.add(isInDeleteSet); diff --git a/data/src/main/java/org/apache/iceberg/data/InternalRecordWrapper.java b/data/src/main/java/org/apache/iceberg/data/InternalRecordWrapper.java index 4aea96d261b7..6378e383f7c1 100644 --- a/data/src/main/java/org/apache/iceberg/data/InternalRecordWrapper.java +++ b/data/src/main/java/org/apache/iceberg/data/InternalRecordWrapper.java @@ -37,9 +37,13 @@ public class InternalRecordWrapper implements StructLike { @SuppressWarnings("unchecked") public InternalRecordWrapper(Types.StructType struct) { - this.transforms = struct.fields().stream() + this(struct.fields().stream() .map(field -> converter(field.type())) - .toArray(length -> (Function[]) Array.newInstance(Function.class, length)); + .toArray(length -> (Function[]) Array.newInstance(Function.class, length))); + } + + private InternalRecordWrapper(Function[] transforms) { + this.transforms = transforms; } private static Function converter(Type type) { @@ -68,6 +72,10 @@ public StructLike get() { return wrapped; } + public InternalRecordWrapper copyFor(StructLike record) { + return new InternalRecordWrapper(transforms).wrap(record); + } + public InternalRecordWrapper wrap(StructLike record) { this.wrapped = record; return this;