diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java index ecd6f74aa261..b1870669e8e6 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java @@ -38,7 +38,7 @@ public final class FilesDelta { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.fs.FilesDelta"); private final ReentrantLock myDataLock = new ReentrantLock(); - private final Set myDeletedPaths = new ObjectLinkedOpenHashSet<>(); + private final Set myDeletedPaths = new ObjectLinkedOpenHashSet<>(FileUtil.PATH_HASHING_STRATEGY); private final Map> myFilesToRecompile = new LinkedHashMap<>(); public void lockData(){ @@ -110,13 +110,13 @@ public final class FilesDelta { if (descriptor != null) { files = myFilesToRecompile.get(descriptor); if (files == null) { - files = new ObjectLinkedOpenHashSet<>(); + files = new ObjectLinkedOpenHashSet<>(FileUtil.FILE_HASHING_STRATEGY); myFilesToRecompile.put(descriptor, files); } } else { LOG.debug("Cannot find root by " + rootId + ", delta will be skipped"); - files = new ObjectLinkedOpenHashSet<>(); + files = new ObjectLinkedOpenHashSet<>(FileUtil.FILE_HASHING_STRATEGY); } int filesCount = in.readInt(); while (filesCount-- > 0) { @@ -222,7 +222,7 @@ public final class FilesDelta { private boolean _addToRecompiled(BuildRootDescriptor root, Collection filesToAdd) { Set files = myFilesToRecompile.get(root); if (files == null) { - files = new ObjectLinkedOpenHashSet<>(); + files = new ObjectLinkedOpenHashSet<>(FileUtil.FILE_HASHING_STRATEGY); myFilesToRecompile.put(root, files); } return files.addAll(filesToAdd); @@ -260,7 +260,7 @@ public final class FilesDelta { lockData(); try { try { - return new ObjectLinkedOpenHashSet<>(myDeletedPaths) ; + return new ObjectLinkedOpenHashSet<>(myDeletedPaths, FileUtil.PATH_HASHING_STRATEGY) ; } finally { myDeletedPaths.clear(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 97113c2db82f..6cda741e2d7a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -220,7 +220,7 @@ public class JavaBuilder extends ModuleLevelBuilder { @NotNull OutputConsumer outputConsumer, @NotNull JavaCompilingTool compilingTool) throws ProjectBuildException, IOException { try { - final Set filesToCompile = new ObjectLinkedOpenHashSet<>(); + final Set filesToCompile = new ObjectLinkedOpenHashSet<>(FileUtil.FILE_HASHING_STRATEGY); dirtyFilesHolder.processDirtyFiles((target, file, descriptor) -> { if (JAVA_SOURCES_FILTER.accept(file) && ourCompilableModuleTypes.contains(target.getModule().getModuleType())) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/OneToManyPathsMapping.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/OneToManyPathsMapping.java index eb479c150bfd..edeb19d05893 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/OneToManyPathsMapping.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/OneToManyPathsMapping.java @@ -4,7 +4,7 @@ package org.jetbrains.jps.incremental.storage; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.Function; import com.intellij.util.containers.JBIterator; -import com.intellij.util.containers.OrderedSet; +import com.intellij.util.containers.ObjectLinkedOpenHashSet; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.IOUtil; import org.jetbrains.annotations.NotNull; @@ -93,7 +93,7 @@ public class OneToManyPathsMapping extends AbstractStateStorage read(@NotNull DataInput in) throws IOException { - final Set result = new OrderedSet<>(FileUtil.PATH_HASHING_STRATEGY); + final Set result = new ObjectLinkedOpenHashSet<>(FileUtil.PATH_HASHING_STRATEGY); final DataInputStream stream = (DataInputStream)in; while (stream.available() > 0) { final String str = IOUtil.readUTF(stream); diff --git a/platform/util/src/com/intellij/util/containers/ObjectLinkedOpenHashSet.java b/platform/util/src/com/intellij/util/containers/ObjectLinkedOpenHashSet.java index e5da3f993d31..fd570d74c5d7 100644 --- a/platform/util/src/com/intellij/util/containers/ObjectLinkedOpenHashSet.java +++ b/platform/util/src/com/intellij/util/containers/ObjectLinkedOpenHashSet.java @@ -15,6 +15,7 @@ */ package com.intellij.util.containers; +import gnu.trove.TObjectHashingStrategy; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -69,10 +70,34 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set */ private static final float DEFAULT_LOAD_FACTOR = .75f; + private final TObjectHashingStrategy defaultHashingStrategy = new TObjectHashingStrategy() { + /** + * The default implementation of TObjectHashingStrategy: + * it delegates hashing to the Object's hashCode method. + */ + @Override + public final int computeHashCode(K o) { + return o != null ? o.hashCode() : 0; + } + + /** + * The default implementation of TObjectHashingStrategy: + * it delegates equality comparisons to the {@link Objects#equals(Object, Object)} + */ + @Override + public final boolean equals(K o1, K o2) { + return Objects.equals(o1, o2); + } + }; + /** * The array of keys. */ transient K[] key; + /** + * The strategy used to hash objects in this collection. + */ + protected final TObjectHashingStrategy hashingStrategy; /** * The mask for wrapping a position counter. */ @@ -121,10 +146,32 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set private final float f; /** - * Creates a new hash set. + * Creates a new hash set. The actual table size will be the least power of two greater than + * {@code expected}/{@code f}. * - *

- * The actual table size will be the least power of two greater than + * @param expected the expected number of elements in the hash set. + * @param f the load factor. + * @param strategy {@link TObjectHashingStrategy} used to compute hash codes and to compare objects. + */ + @SuppressWarnings("unchecked") + public ObjectLinkedOpenHashSet(final int expected, final float f, TObjectHashingStrategy strategy) { + if (f <= 0 || f > 1) { + throw new IllegalArgumentException("Load factor must be greater than 0 and smaller than or equal to 1"); + } + if (expected < 0) { + throw new IllegalArgumentException("The expected number of elements must be nonnegative"); + } + this.f = f; + minN = n = arraySize(expected, f); + mask = n - 1; + maxFill = maxFill(n, f); + key = (K[])new Object[n + 1]; + link = new long[n + 1]; + hashingStrategy = strategy; + } + + /** + * Creates a new hash set. The actual table size will be the least power of two greater than * {@code expected}/{@code f}. * * @param expected the expected number of elements in the hash set. @@ -144,6 +191,7 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set maxFill = maxFill(n, f); key = (K[])new Object[n + 1]; link = new long[n + 1]; + hashingStrategy = defaultHashingStrategy; } /** @@ -155,6 +203,16 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set this(expected, DEFAULT_LOAD_FACTOR); } + /** + * Creates a new hash set with {@link ObjectLinkedOpenHashSet#DEFAULT_LOAD_FACTOR} as load factor. + * + * @param expected the expected number of elements in the hash set. + * @param strategy {@link TObjectHashingStrategy} used to compute hash codes and to compare objects. + */ + public ObjectLinkedOpenHashSet(final int expected, TObjectHashingStrategy strategy) { + this(expected, DEFAULT_LOAD_FACTOR, strategy); + } + /** * Creates a new hash set with initial expected * {@link ObjectLinkedOpenHashSet#DEFAULT_INITIAL_SIZE} elements and @@ -164,6 +222,14 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR); } + /** + * Creates a new hash set with initial expected + * @param strategy {@link TObjectHashingStrategy} used to compute hash codes and to compare objects. + */ + public ObjectLinkedOpenHashSet(TObjectHashingStrategy strategy) { + this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR, strategy); + } + /** * Creates a new hash set with {@link ObjectLinkedOpenHashSet#DEFAULT_LOAD_FACTOR} as load factor * copying a given collection. @@ -175,6 +241,18 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set addAll(c); } + /** + * Creates a new hash set with {@link ObjectLinkedOpenHashSet#DEFAULT_LOAD_FACTOR} as load factor + * copying a given collection. + * + * @param c a {@link Collection} to be copied into the new hash set. + * @param strategy {@link TObjectHashingStrategy} used to compute hash codes and to compare objects. + */ + public ObjectLinkedOpenHashSet(final Collection c, TObjectHashingStrategy strategy) { + this(c.size(), DEFAULT_LOAD_FACTOR , strategy); + addAll(c); + } + private int realSize() { return containsNull ? size - 1 : size; } @@ -220,12 +298,12 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set K curr; final K[] key = this.key; // The starting point. - if ((curr = key[pos = mix(k.hashCode()) & mask]) != null) { - if (curr.equals(k)) { + if ((curr = key[pos = mix(hashingStrategy.computeHashCode(k)) & mask]) != null) { + if (hashingStrategy.equals(curr,k)) { return false; } while ((curr = key[pos = pos + 1 & mask]) != null) { - if (curr.equals(k)) { + if (hashingStrategy.equals(curr, k)) { return false; } } @@ -275,12 +353,12 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set K curr; final K[] key = this.key; // The starting point. - if ((curr = key[pos = mix(k.hashCode()) & mask]) != null) { - if (curr.equals(k)) { + if ((curr = key[pos = mix(hashingStrategy.computeHashCode(k)) & mask]) != null) { + if (hashingStrategy.equals(curr, k)) { return curr; } while ((curr = key[pos = pos + 1 & mask]) != null) { - if (curr.equals(k)) { + if (hashingStrategy.equals(curr, k)) { return curr; } } @@ -322,7 +400,7 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set key[last] = null; return; } - slot = mix(curr.hashCode()) & mask; + slot = mix(hashingStrategy.computeHashCode(curr)) & mask; if (last <= pos ? last >= slot || slot > pos : last >= slot && slot > pos) { break; } @@ -361,14 +439,15 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set return false; } K curr; - final K[] key = this.key; + K key = (K)k; + final K[] keyArray = this.key; int pos; // The starting point. - if ((curr = key[pos = mix(k.hashCode()) & mask]) == null) return false; - if (k.equals(curr)) return removeEntry(pos); + if ((curr = keyArray[pos = mix(hashingStrategy.computeHashCode(key)) & mask]) == null) return false; + if (hashingStrategy.equals(key, curr)) return removeEntry(pos); while (true) { - if ((curr = key[pos = pos + 1 & mask]) == null) return false; - if (k.equals(curr)) return removeEntry(pos); + if ((curr = keyArray[pos = pos + 1 & mask]) == null) return false; + if (hashingStrategy.equals(key, curr)) return removeEntry(pos); } } @@ -378,14 +457,15 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set return containsNull; } K curr; - final K[] key = this.key; + K key = (K)k; + final K[] keyArray = this.key; int pos; // The starting point. - if ((curr = key[pos = mix(k.hashCode()) & mask]) == null) return false; - if (k.equals(curr)) return true; + if ((curr = keyArray[pos = mix(hashingStrategy.computeHashCode(key)) & mask]) == null) return false; + if (hashingStrategy.equals(key, curr)) return true; while (true) { - if ((curr = key[pos = pos + 1 & mask]) == null) return false; - if (k.equals(curr)) return true; + if ((curr = keyArray[pos = pos + 1 & mask]) == null) return false; + if (hashingStrategy.equals(key, curr)) return true; } } @@ -400,15 +480,16 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set if (k == null) return key[n]; // This is correct independently of the value of containsNull and of the set // being custom K curr; - final K[] key = this.key; + K key = (K)k; + final K[] keyArray = this.key; int pos; // The starting point. - if ((curr = key[pos = mix(k.hashCode()) & mask]) == null) return null; - if (k.equals(curr)) return curr; + if ((curr = keyArray[pos = mix(hashingStrategy.computeHashCode(key)) & mask]) == null) return null; + if (hashingStrategy.equals(key, curr)) return curr; // There's always an unused entry. while (true) { - if ((curr = key[pos = pos + 1 & mask]) == null) return null; - if (k.equals(curr)) return curr; + if ((curr = keyArray[pos = pos + 1 & mask]) == null) return null; + if (hashingStrategy.equals(key, curr)) return curr; } } @@ -662,7 +743,7 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set key[last] = null; return; } - slot = mix(curr.hashCode()) & mask; + slot = mix(hashingStrategy.computeHashCode(curr)) & mask; if (last <= pos ? last >= slot || slot > pos : last >= slot && slot > pos) { break; } @@ -749,7 +830,7 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set pos = newN; } else { - pos = mix(key[i].hashCode()) & mask; + pos = mix(hashingStrategy.computeHashCode(key[i])) & mask; while (newKey[pos] != null) { pos = pos + 1 & mask; } @@ -825,7 +906,7 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set i++; } if (this != key[i]) { - h += key[i].hashCode(); + h += hashingStrategy.computeHashCode(key[i]); } i++; } @@ -901,7 +982,7 @@ public class ObjectLinkedOpenHashSet extends AbstractSet implements Set containsNull = true; } else { - if (key[pos = mix(k.hashCode()) & mask] != null) { + if (key[pos = mix(hashingStrategy.computeHashCode(k)) & mask] != null) { while (key[pos = pos + 1 & mask] != null) ; } } diff --git a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java index 1fe5d79de2b5..9e8b25dd67f4 100644 --- a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java +++ b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java @@ -6,7 +6,8 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileFilters; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.containers.ObjectLinkedOpenHashSet; +import gnu.trove.THashMap; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; @@ -88,9 +89,9 @@ public class FormsBindingManager extends FormsBuilder { return exitCode; } - final Map filesToCompile = new LinkedHashMap<>(); - final Map formsToCompile = new LinkedHashMap<>(); - final Map> srcToForms = new LinkedHashMap<>(); + final Map filesToCompile = new THashMap<>(FileUtil.FILE_HASHING_STRATEGY); + final Map formsToCompile = new THashMap<>(FileUtil.FILE_HASHING_STRATEGY); + final Map> srcToForms = new THashMap<>(FileUtil.FILE_HASHING_STRATEGY); if (!JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) && config.isInstrumentClasses() && FORCE_FORMS_REBUILD_FLAG.get(context, Boolean.FALSE)) { // force compilation of all forms, but only once per chunk @@ -208,7 +209,7 @@ public class FormsBindingManager extends FormsBuilder { } } - final Set candidates = new ObjectLinkedOpenHashSet<>(); + final Set candidates = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY); for (JavaSourceRootDescriptor rd : targetRoots) { candidates.addAll(findPossibleSourcesForClass(rd, className)); } diff --git a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsInstrumenter.java b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsInstrumenter.java index fc087f65b953..c473443e9f1c 100644 --- a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsInstrumenter.java +++ b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsInstrumenter.java @@ -11,7 +11,8 @@ import com.intellij.uiDesigner.compiler.*; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.lw.CompiledClassPropertiesProvider; import com.intellij.uiDesigner.lw.LwRootContainer; -import com.intellij.util.containers.ObjectLinkedOpenHashSet; +import gnu.trove.THashMap; +import gnu.trove.THashSet; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.ProjectPaths; @@ -61,7 +62,7 @@ public class FormsInstrumenter extends FormsBuilder { return ExitCode.NOTHING_DONE; } - final Set formsToCompile = new ObjectLinkedOpenHashSet<>(); + final Set formsToCompile = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY); for (Collection files : srcToForms.values()) { formsToCompile.addAll(files); } @@ -124,7 +125,7 @@ public class FormsInstrumenter extends FormsBuilder { CompileContext context, ModuleChunk chunk, final Map chunkSourcePath, final InstrumentationClassFinder finder, Collection forms, OutputConsumer outConsumer ) throws ProjectBuildException { - final Map> instrumented = new LinkedHashMap<>(); + final Map> instrumented = new THashMap<>(FileUtil.FILE_HASHING_STRATEGY); final Map class2form = new HashMap<>(); final MyNestedFormLoader nestedFormsLoader = new MyNestedFormLoader(chunkSourcePath, ProjectPaths.getOutputPathsWithDependents(chunk), finder);