diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java index 029c78953..f7fe5c363 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java @@ -43,7 +43,7 @@ import java.util.Optional; */ public class ChunkBatchingExtent extends AbstractBufferingExtent { - private final BlockMap blockMap = BlockMap.create(); + private final BlockMap blockMap = BlockMap.createForBaseBlock(); private boolean enabled; public ChunkBatchingExtent(Extent extent) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java index 733d6c7de..c55cb694d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java @@ -142,7 +142,7 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde priorityMap.put(BlockTypes.MOVING_PISTON, PlacementPriority.FINAL); } - private Map stages = new HashMap<>(); + private Map> stages = new HashMap<>(); private boolean enabled; @@ -176,7 +176,7 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde this.enabled = enabled; for (PlacementPriority priority : PlacementPriority.values()) { - stages.put(priority, BlockMap.create()); + stages.put(priority, BlockMap.createForBaseBlock()); } } @@ -261,7 +261,7 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde } List operations = new ArrayList<>(); for (PlacementPriority priority : PlacementPriority.values()) { - BlockMap blocks = stages.get(priority); + BlockMap blocks = stages.get(priority); operations.add(new SetBlockMap(getExtent(), blocks) { @Override public Operation resume(RunContext run) throws WorldEditException { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java index 863aadd03..99607ae16 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java @@ -34,9 +34,9 @@ import static com.google.common.base.Preconditions.checkNotNull; public class SetBlockMap implements Operation { private final Extent extent; - private final BlockMap blocks; + private final BlockMap blocks; - public SetBlockMap(Extent extent, BlockMap blocks) { + public SetBlockMap(Extent extent, BlockMap blocks) { this.extent = checkNotNull(extent); this.blocks = checkNotNull(blocks); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java index 2620a906e..563c13de6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -40,6 +41,7 @@ import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; import static com.sk89q.worldedit.math.BitMath.fixSign; import static com.sk89q.worldedit.math.BitMath.mask; @@ -47,7 +49,7 @@ import static com.sk89q.worldedit.math.BitMath.mask; /** * A space-efficient map implementation for block locations. */ -public class BlockMap extends AbstractMap { +public class BlockMap extends AbstractMap { /* ========================= IF YOU MAKE CHANGES TO THIS CLASS @@ -55,12 +57,20 @@ public class BlockMap extends AbstractMap { Or just temporarily remove the annotation disabling the related tests. ========================= */ - public static BlockMap create() { - return new BlockMap(); + public static BlockMap create() { + return create(() -> new Int2ObjectOpenHashMap<>(64, 1f)); } - public static BlockMap copyOf(Map source) { - return new BlockMap(source); + public static BlockMap createForBaseBlock() { + return create(Int2BaseBlockMap::new); + } + + private static BlockMap create(Supplier> subMapSupplier) { + return new BlockMap<>(subMapSupplier); + } + + public static BlockMap copyOf(Map source) { + return new BlockMap<>(Int2ObjectOpenHashMap::new, source); } /* @@ -108,31 +118,34 @@ public class BlockMap extends AbstractMap { return BlockVector3.at(x, y, z); } - private final Long2ObjectMap maps = new Long2ObjectOpenHashMap<>(4, 1f); - private Set> entrySet; - private Collection values; + private final Long2ObjectMap> maps = new Long2ObjectOpenHashMap<>(4, 1f); + private final Supplier> subMapSupplier; + private Set> entrySet; + private Collection values; - private BlockMap() { + private BlockMap(Supplier> subMapSupplier) { + this.subMapSupplier = subMapSupplier; } - private BlockMap(Map source) { + private BlockMap(Supplier> subMapSupplier, Map source) { + this.subMapSupplier = subMapSupplier; putAll(source); } - private SubBlockMap getOrCreateMap(long groupKey) { - return maps.computeIfAbsent(groupKey, k -> new SubBlockMap()); + private Int2ObjectMap getOrCreateMap(long groupKey) { + return maps.computeIfAbsent(groupKey, k -> subMapSupplier.get()); } - private SubBlockMap getOrEmptyMap(long groupKey) { - return maps.getOrDefault(groupKey, SubBlockMap.EMPTY); + private Int2ObjectMap getOrEmptyMap(long groupKey) { + return maps.getOrDefault(groupKey, Int2ObjectMaps.emptyMap()); } /** * Apply the function the the map at {@code groupKey}, and if the function empties the map, * delete it from {@code maps}. */ - private R cleanlyModifyMap(long groupKey, Function, R> func) { - SubBlockMap map = maps.get(groupKey); + private R cleanlyModifyMap(long groupKey, Function, R> func) { + Int2ObjectMap map = maps.get(groupKey); if (map != null) { R result = func.apply(map); if (map.isEmpty()) { @@ -140,7 +153,7 @@ public class BlockMap extends AbstractMap { } return result; } - map = new SubBlockMap(); + map = subMapSupplier.get(); R result = func.apply(map); if (!map.isEmpty()) { maps.put(groupKey, map); @@ -149,19 +162,19 @@ public class BlockMap extends AbstractMap { } @Override - public BaseBlock put(BlockVector3 key, BaseBlock value) { + public V put(BlockVector3 key, V value) { return getOrCreateMap(toGroupKey(key)).put(toInnerKey(key), value); } @Override - public BaseBlock getOrDefault(Object key, BaseBlock defaultValue) { + public V getOrDefault(Object key, V defaultValue) { BlockVector3 vec = (BlockVector3) key; return getOrEmptyMap(toGroupKey(vec)) .getOrDefault(toInnerKey(vec), defaultValue); } @Override - public void forEach(BiConsumer action) { + public void forEach(BiConsumer action) { maps.forEach((groupKey, m) -> m.forEach((innerKey, block) -> action.accept(reconstructLocation(groupKey, innerKey), block) @@ -170,7 +183,7 @@ public class BlockMap extends AbstractMap { } @Override - public void replaceAll(BiFunction function) { + public void replaceAll(BiFunction function) { maps.forEach((groupKey, m) -> m.replaceAll((innerKey, block) -> function.apply(reconstructLocation(groupKey, innerKey), block) @@ -179,7 +192,7 @@ public class BlockMap extends AbstractMap { } @Override - public BaseBlock putIfAbsent(BlockVector3 key, BaseBlock value) { + public V putIfAbsent(BlockVector3 key, V value) { return getOrCreateMap(toGroupKey(key)).putIfAbsent(toInnerKey(key), value); } @@ -191,66 +204,66 @@ public class BlockMap extends AbstractMap { } @Override - public boolean replace(BlockVector3 key, BaseBlock oldValue, BaseBlock newValue) { + public boolean replace(BlockVector3 key, V oldValue, V newValue) { return cleanlyModifyMap(toGroupKey(key), map -> map.replace(toInnerKey(key), oldValue, newValue)); } @Override - public BaseBlock replace(BlockVector3 key, BaseBlock value) { + public V replace(BlockVector3 key, V value) { return getOrCreateMap(toGroupKey(key)).replace(toInnerKey(key), value); } @Override - public BaseBlock computeIfAbsent(BlockVector3 key, Function mappingFunction) { + public V computeIfAbsent(BlockVector3 key, Function mappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.computeIfAbsent(toInnerKey(key), ik -> mappingFunction.apply(key))); } @Override - public BaseBlock computeIfPresent(BlockVector3 key, BiFunction remappingFunction) { + public V computeIfPresent(BlockVector3 key, BiFunction remappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.computeIfPresent(toInnerKey(key), (ik, block) -> remappingFunction.apply(key, block))); } @Override - public BaseBlock compute(BlockVector3 key, BiFunction remappingFunction) { + public V compute(BlockVector3 key, BiFunction remappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.compute(toInnerKey(key), (ik, block) -> remappingFunction.apply(key, block))); } @Override - public BaseBlock merge(BlockVector3 key, BaseBlock value, BiFunction remappingFunction) { + public V merge(BlockVector3 key, V value, BiFunction remappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.merge(toInnerKey(key), value, remappingFunction)); } @Override - public Set> entrySet() { - Set> es = entrySet; + public Set> entrySet() { + Set> es = entrySet; if (es == null) { - entrySet = es = new AbstractSet>() { + entrySet = es = new AbstractSet>() { @Override - public Iterator> iterator() { - return new AbstractIterator>() { + public Iterator> iterator() { + return new AbstractIterator>() { - private final ObjectIterator> primaryIterator + private final ObjectIterator>> primaryIterator = Long2ObjectMaps.fastIterator(maps); private long currentGroupKey; - private ObjectIterator> secondaryIterator; + private ObjectIterator> secondaryIterator; @Override - protected Entry computeNext() { + protected Entry computeNext() { if (secondaryIterator == null || !secondaryIterator.hasNext()) { if (!primaryIterator.hasNext()) { return endOfData(); } - Long2ObjectMap.Entry next = primaryIterator.next(); + Long2ObjectMap.Entry> next = primaryIterator.next(); currentGroupKey = next.getLongKey(); secondaryIterator = Int2ObjectMaps.fastIterator(next.getValue()); } - Int2ObjectMap.Entry next = secondaryIterator.next(); + Int2ObjectMap.Entry next = secondaryIterator.next(); return new LazyEntry(currentGroupKey, next.getIntKey(), next.getValue()); } }; @@ -265,14 +278,14 @@ public class BlockMap extends AbstractMap { return es; } - private final class LazyEntry implements Map.Entry { + private final class LazyEntry implements Entry { private final long groupKey; private final int innerKey; private BlockVector3 lazyKey; - private BaseBlock value; + private V value; - private LazyEntry(long groupKey, int innerKey, BaseBlock value) { + private LazyEntry(long groupKey, int innerKey, V value) { this.groupKey = groupKey; this.innerKey = innerKey; this.value = value; @@ -288,12 +301,12 @@ public class BlockMap extends AbstractMap { } @Override - public BaseBlock getValue() { + public V getValue() { return value; } @Override - public BaseBlock setValue(BaseBlock value) { + public V setValue(V value) { this.value = value; return getOrCreateMap(groupKey).put(innerKey, value); } @@ -301,8 +314,9 @@ public class BlockMap extends AbstractMap { public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry e = (Map.Entry) o; - if (o instanceof LazyEntry) { + Entry e = (Entry) o; + if (o instanceof BlockMap.LazyEntry) { + @SuppressWarnings("unchecked") LazyEntry otherE = (LazyEntry) o; return otherE.groupKey == groupKey && otherE.innerKey == innerKey @@ -330,7 +344,7 @@ public class BlockMap extends AbstractMap { @Override public boolean containsKey(Object key) { BlockVector3 vec = (BlockVector3) key; - Map activeMap = maps.get(toGroupKey(vec)); + Int2ObjectMap activeMap = maps.get(toGroupKey(vec)); if (activeMap == null) { return false; } @@ -338,9 +352,9 @@ public class BlockMap extends AbstractMap { } @Override - public BaseBlock get(Object key) { + public V get(Object key) { BlockVector3 vec = (BlockVector3) key; - Map activeMap = maps.get(toGroupKey(vec)); + Int2ObjectMap activeMap = maps.get(toGroupKey(vec)); if (activeMap == null) { return null; } @@ -348,24 +362,25 @@ public class BlockMap extends AbstractMap { } @Override - public BaseBlock remove(Object key) { + public V remove(Object key) { BlockVector3 vec = (BlockVector3) key; - Map activeMap = maps.get(toGroupKey(vec)); + Int2ObjectMap activeMap = maps.get(toGroupKey(vec)); if (activeMap == null) { return null; } - BaseBlock removed = activeMap.remove(toInnerKey(vec)); + V removed = activeMap.remove(toInnerKey(vec)); if (activeMap.isEmpty()) { maps.remove(toGroupKey(vec)); } return removed; } + @SuppressWarnings("unchecked") @Override - public void putAll(Map m) { + public void putAll(Map m) { if (m instanceof BlockMap) { // optimize insertions: - ((BlockMap) m).maps.forEach((groupKey, map) -> + ((BlockMap) m).maps.forEach((groupKey, map) -> getOrCreateMap(groupKey).putAll(map) ); } else { @@ -387,12 +402,12 @@ public class BlockMap extends AbstractMap { // we can optimize values access though, by skipping BV construction. @Override - public Collection values() { - Collection vs = values; + public Collection values() { + Collection vs = values; if (vs == null) { - values = vs = new AbstractCollection() { + values = vs = new AbstractCollection() { @Override - public Iterator iterator() { + public Iterator iterator() { return maps.values().stream() .flatMap(m -> m.values().stream()) .iterator(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/SubBlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java similarity index 93% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/SubBlockMap.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java index 440f52eee..36d459c12 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/SubBlockMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java @@ -39,7 +39,7 @@ import java.util.function.BiFunction; /** * Int-to-BaseBlock map, but with optimizations for common cases. */ -class SubBlockMap extends AbstractInt2ObjectMap { +class Int2BaseBlockMap extends AbstractInt2ObjectMap { private static boolean hasInt(BlockState b) { return BlockStateIdAccess.getBlockStateId(b).isPresent(); @@ -65,7 +65,7 @@ class SubBlockMap extends AbstractInt2ObjectMap { return state.toBaseBlock(); } - static final SubBlockMap EMPTY = new SubBlockMap(); + static final Int2BaseBlockMap EMPTY = new Int2BaseBlockMap(); private final Int2IntMap commonMap = new Int2IntOpenHashMap(64, 1f); private final Int2ObjectMap uncommonMap = new Int2ObjectOpenHashMap<>(1, 1f); @@ -88,7 +88,7 @@ class SubBlockMap extends AbstractInt2ObjectMap { private final ObjectIterator commonIter = Int2IntMaps.fastIterator(commonMap); - private final ObjectIterator> uncommonIter + private final ObjectIterator> uncommonIter = Int2ObjectMaps.fastIterator(uncommonMap); @Override @@ -114,7 +114,7 @@ class SubBlockMap extends AbstractInt2ObjectMap { @Override public int size() { - return SubBlockMap.this.size(); + return Int2BaseBlockMap.this.size(); } }; } @@ -179,9 +179,9 @@ class SubBlockMap extends AbstractInt2ObjectMap { next.setValue(assumeAsInt(value.toImmutableState())); } } - for (ObjectIterator> iter = Int2ObjectMaps.fastIterator(uncommonMap); + for (ObjectIterator> iter = Int2ObjectMaps.fastIterator(uncommonMap); iter.hasNext(); ) { - Int2ObjectMap.Entry next = iter.next(); + Entry next = iter.next(); BaseBlock value = function.apply(next.getIntKey(), next.getValue()); if (isUncommon(value)) { next.setValue(value); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java index e8d11d2e2..55c7f1a21 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java @@ -37,7 +37,7 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class LocatedBlockList implements Iterable { - private final BlockMap blocks = BlockMap.create(); + private final BlockMap blocks = BlockMap.createForBaseBlock(); private final PositionList order = PositionList.create( WorldEdit.getInstance().getConfiguration().extendedYLimit ); diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java index 4a6302e5b..cab27786f 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java @@ -110,7 +110,7 @@ class BlockMapTest { private final BaseBlock air = checkNotNull(BlockTypes.AIR).getDefaultState().toBaseBlock(); private final BaseBlock oakWood = checkNotNull(BlockTypes.OAK_WOOD).getDefaultState().toBaseBlock(); - private BlockMap map = BlockMap.create(); + private BlockMap map = BlockMap.createForBaseBlock(); @BeforeEach void setUp() {