extract IntObjectMap/ObjectIntMap interfaces to generalize custom maps

This commit is contained in:
Alexey Kudravtsev
2017-11-14 14:47:21 +03:00
parent 367716f390
commit c185e5ca59
26 changed files with 546 additions and 147 deletions

View File

@@ -53,8 +53,8 @@ import com.intellij.util.ExceptionUtil;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.containers.ConcurrentBitSet;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.messages.MessageBus;
import gnu.trove.*;
@@ -451,7 +451,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
assert !myApplication.isDispatchThread();
final int resolvedInPreviousBatch = this.resolvedInPreviousBatch;
final int totalSize = files.size() + resolvedInPreviousBatch;
final ConcurrentIntObjectMap<int[]> fileToForwardIds = ContainerUtil.createConcurrentIntObjectMap();
final IntObjectMap<int[]> fileToForwardIds = ContainerUtil.createConcurrentIntObjectMap();
final Set<VirtualFile> toProcess = Collections.synchronizedSet(files);
indicator.setIndeterminate(false);
ProgressIndicatorUtils.forceWriteActionPriority(indicator, (Disposable)indicator);
@@ -652,11 +652,11 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable
return forwardIds;
}
private void storeIds(@NotNull ConcurrentIntObjectMap<int[]> fileToForwardIds) {
private void storeIds(@NotNull IntObjectMap<int[]> fileToForwardIds) {
int forwardSize = 0;
int backwardSize = 0;
final TIntObjectHashMap<TIntArrayList> fileToBackwardIds = new TIntObjectHashMap<>(fileToForwardIds.size());
for (ConcurrentIntObjectMap.IntEntry<int[]> entry : fileToForwardIds.entries()) {
for (IntObjectMap.Entry<int[]> entry : fileToForwardIds.entries()) {
int fileId = entry.getKey();
int[] forwardIds = entry.getValue();
forwardSize += forwardIds.length;

View File

@@ -1,8 +1,8 @@
package org.jetbrains.io.jsonRpc;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
@@ -13,12 +13,12 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.AsyncPromise;
import java.util.Enumeration;
import java.util.Collection;
public abstract class Client extends UserDataHolderBase {
protected final Channel channel;
final ConcurrentIntObjectMap<AsyncPromise<Object>> messageCallbackMap = ContainerUtil.createConcurrentIntObjectMap();
final IntObjectMap<AsyncPromise<Object>> messageCallbackMap = ContainerUtil.createConcurrentIntObjectMap();
protected Client(@NotNull Channel channel) {
this.channel = channel;
@@ -55,10 +55,10 @@ public abstract class Client extends UserDataHolderBase {
final void rejectAsyncResults(@NotNull ExceptionHandler exceptionHandler) {
if (!messageCallbackMap.isEmpty()) {
Enumeration<AsyncPromise<Object>> elements = messageCallbackMap.elements();
while (elements.hasMoreElements()) {
Collection<AsyncPromise<Object>> elements = messageCallbackMap.values();
for (AsyncPromise<Object> element : elements) {
try {
elements.nextElement().setError("rejected");
element.setError("rejected");
}
catch (Throwable e) {
exceptionHandler.exceptionCaught(e);
@@ -69,9 +69,9 @@ public abstract class Client extends UserDataHolderBase {
private static final class ChannelFutureAwarePromise<T> extends AsyncPromise<T> implements ChannelFutureListener {
private final int messageId;
private final ConcurrentIntObjectMap<?> messageCallbackMap;
private final IntObjectMap<?> messageCallbackMap;
public ChannelFutureAwarePromise(int messageId, ConcurrentIntObjectMap<?> messageCallbackMap) {
public ChannelFutureAwarePromise(int messageId, IntObjectMap<?> messageCallbackMap) {
this.messageId = messageId;
this.messageCallbackMap = messageCallbackMap;
}

View File

@@ -15,8 +15,8 @@
*/
package com.intellij.openapi.util;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -37,16 +37,16 @@ public interface Iconable {
Icon getIcon(@IconFlags int flags);
class LastComputedIcon {
private static final Key<ConcurrentIntObjectMap<Icon>> LAST_COMPUTED_ICON = Key.create("lastComputedIcon");
private static final Key<IntObjectMap<Icon>> LAST_COMPUTED_ICON = Key.create("lastComputedIcon");
@Nullable
public static Icon get(@NotNull UserDataHolder holder, int flags) {
ConcurrentIntObjectMap<Icon> map = holder.getUserData(LAST_COMPUTED_ICON);
IntObjectMap<Icon> map = holder.getUserData(LAST_COMPUTED_ICON);
return map == null ? null : map.get(flags);
}
public static void put(@NotNull UserDataHolder holder, Icon icon, int flags) {
ConcurrentIntObjectMap<Icon> map = holder.getUserData(LAST_COMPUTED_ICON);
IntObjectMap<Icon> map = holder.getUserData(LAST_COMPUTED_ICON);
if (icon == null) {
if (map != null) {
map.remove(flags);

View File

@@ -17,10 +17,9 @@
package com.intellij.util.indexing;
import com.intellij.openapi.application.PathManager;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntProcedure;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -32,8 +31,8 @@ import java.io.*;
* Date: Feb 12, 2008
*/
public class ID<K, V> extends IndexId<K,V> {
private static final ConcurrentIntObjectMap<ID> ourRegistry = ContainerUtil.createConcurrentIntObjectMap();
private static final TObjectIntHashMap<String> ourNameToIdRegistry = new TObjectIntHashMap<String>();
private static final IntObjectMap<ID> ourRegistry = ContainerUtil.createConcurrentIntObjectMap();
private static final TObjectIntHashMap<String> ourNameToIdRegistry = new TObjectIntHashMap<>();
static final int MAX_NUMBER_OF_INDICES = Short.MAX_VALUE;
private final short myUniqueId;
@@ -41,30 +40,23 @@ public class ID<K, V> extends IndexId<K,V> {
static {
final File indices = getEnumFile();
try {
final BufferedReader reader = new BufferedReader(new FileReader(indices));
TObjectIntHashMap<String> nameToIdRegistry = new TObjectIntHashMap<String>();
try {
TObjectIntHashMap<String> nameToIdRegistry = new TObjectIntHashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader(indices))) {
int cnt = 0;
do {
cnt++;
final String name = reader.readLine();
if (name == null) break;
nameToIdRegistry.put(name, cnt);
}
while (true);
}
finally {
reader.close();
cnt++;
final String name = reader.readLine();
if (name == null) break;
nameToIdRegistry.put(name, cnt);
}
while (true);
}
synchronized (ourNameToIdRegistry) {
ourNameToIdRegistry.ensureCapacity(nameToIdRegistry.size());
nameToIdRegistry.forEachEntry(new TObjectIntProcedure<String>() {
@Override
public boolean execute(String name, int index) {
ourNameToIdRegistry.put(name, index);
return true;
}
nameToIdRegistry.forEachEntry((name, index) -> {
ourNameToIdRegistry.put(name, index);
return true;
});
}
}
@@ -113,25 +105,18 @@ public class ID<K, V> extends IndexId<K,V> {
private static void writeEnumFile() {
try {
final File f = getEnumFile();
final BufferedWriter w = new BufferedWriter(new FileWriter(f));
try {
try (BufferedWriter w = new BufferedWriter(new FileWriter(f))) {
final String[] names = new String[ourNameToIdRegistry.size()];
ourNameToIdRegistry.forEachEntry(new TObjectIntProcedure<String>() {
@Override
public boolean execute(final String key, final int value) {
names[value - 1] = key;
return true;
}
});
ourNameToIdRegistry.forEachEntry((key, value) -> {
names[value - 1] = key;
return true;
});
for (String name : names) {
w.write(name);
w.newLine();
}
}
finally {
w.close();
w.write(name);
w.newLine();
}
}
}
catch (IOException e) {
@@ -142,7 +127,7 @@ public class ID<K, V> extends IndexId<K,V> {
@NotNull
public static <K, V> ID<K, V> create(@NonNls @NotNull String name) {
final ID<K, V> found = findByName(name);
return found != null ? found : new ID<K, V>(name);
return found != null ? found : new ID<>(name);
}
@Nullable

View File

@@ -18,8 +18,8 @@ package com.intellij.openapi.module.impl.scopes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.impl.ModuleScopeProvider;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import org.jetbrains.annotations.NotNull;
/**
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
*/
public class ModuleScopeProviderImpl implements ModuleScopeProvider {
private final Module myModule;
private final ConcurrentIntObjectMap<GlobalSearchScope> myScopeCache = ContainerUtil.createConcurrentIntObjectMap();
private final IntObjectMap<GlobalSearchScope> myScopeCache = ContainerUtil.createConcurrentIntObjectMap();
private ModuleWithDependentsTestScope myModuleTestsWithDependentsScope;
public ModuleScopeProviderImpl(@NotNull Module module) {

View File

@@ -69,8 +69,8 @@ import com.intellij.ui.ReplacePromptDialog;
import com.intellij.usages.ChunkExtractor;
import com.intellij.usages.UsageViewManager;
import com.intellij.usages.impl.SyntaxHighlighterOverEditorHighlighter;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.containers.Predicate;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.text.CharArrayUtil;
@@ -786,7 +786,7 @@ public class FindManagerImpl extends FindManager {
}
}
private static final ConcurrentIntObjectMap<Boolean> ourReportedPatterns = ContainerUtil.createConcurrentIntObjectMap();
private static final IntObjectMap<Boolean> ourReportedPatterns = ContainerUtil.createConcurrentIntObjectMap();
private static Matcher compileRegExp(FindModel model, CharSequence text) {
Pattern pattern = model.compileRegExp();

View File

@@ -34,8 +34,8 @@ import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.Query;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -118,7 +118,7 @@ public class DirectoryIndexImpl extends DirectoryIndex {
protected RootIndex.InfoCache createRootInfoCache() {
return new RootIndex.InfoCache() {
// Upsource can't use int-mapping because different files may have the same id there
private final ConcurrentIntObjectMap<DirectoryInfo> myInfoCache = ContainerUtil.createConcurrentIntObjectMap();
private final IntObjectMap<DirectoryInfo> myInfoCache = ContainerUtil.createConcurrentIntObjectMap();
@Override
public void cacheInfo(@NotNull VirtualFile dir, @NotNull DirectoryInfo info) {
myInfoCache.put(((NewVirtualFile)dir).getId(), info);

View File

@@ -28,8 +28,8 @@ import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.messages.MessageBusConnection;
import gnu.trove.THashMap;
@@ -271,7 +271,7 @@ public class SemServiceImpl extends SemService{
}
private static class SemCacheChunk {
private final ConcurrentIntObjectMap<List<SemElement>> map = ContainerUtil.createConcurrentIntObjectMap();
private final IntObjectMap<List<SemElement>> map = ContainerUtil.createConcurrentIntObjectMap();
public List<SemElement> getSemElements(SemKey<?> key) {
return map.get(key.getUniqueId());

View File

@@ -75,8 +75,8 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.SerializationManagerEx;
import com.intellij.util.*;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.gist.GistManager;
import com.intellij.util.gist.GistManagerImpl;
import com.intellij.util.indexing.impl.InvertedIndexValueIterator;
@@ -1830,7 +1830,7 @@ public class FileBasedIndexImpl extends FileBasedIndex implements BaseComponent,
}
private final class ChangedFilesCollector extends IndexedFilesListener {
private final ConcurrentIntObjectMap<VirtualFile> myFilesToUpdate = ContainerUtil.createConcurrentIntObjectMap();
private final IntObjectMap<VirtualFile> myFilesToUpdate = ContainerUtil.createConcurrentIntObjectMap();
private final VfsEventsMerger myVfsEventsMerger = new VfsEventsMerger();
private final Phaser myWorkersFinishedSync = new Phaser() {
@Override

View File

@@ -24,8 +24,8 @@ import com.intellij.openapi.vfs.newvfs.persistent.FSRecords;
import com.intellij.psi.stubs.StubIndexKey;
import com.intellij.util.SmartList;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.io.DataInputOutputUtil;
import gnu.trove.TObjectLongHashMap;
import gnu.trove.TObjectLongProcedure;
@@ -340,7 +340,7 @@ public class IndexingStamp {
}
}
private static final ConcurrentIntObjectMap<Timestamps> myTimestampsCache = ContainerUtil.createConcurrentIntObjectMap();
private static final IntObjectMap<IndexingStamp.Timestamps> myTimestampsCache = ContainerUtil.createConcurrentIntObjectMap();
private static final BlockingQueue<Integer> ourFinishedFiles = new ArrayBlockingQueue<>(100);
public static long getIndexStamp(int fileId, ID<?, ?> indexName) {

View File

@@ -26,8 +26,8 @@ import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,7 +40,7 @@ import java.util.List;
* @author gregsh
*/
public final class LightDirectoryIndex<T> {
private final ConcurrentIntObjectMap<T> myInfoCache = ContainerUtil.createConcurrentIntObjectMap();
private final IntObjectMap<T> myInfoCache = ContainerUtil.createConcurrentIntObjectMap();
private final T myDefValue;
private final Consumer<LightDirectoryIndex<T>> myInitializer;

View File

@@ -18,8 +18,8 @@ package com.intellij.util.indexing;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -87,7 +87,7 @@ public class VfsEventsMerger {
return myChangeInfos.values().stream().map(ChangeInfo::getFile);
}
private final ConcurrentIntObjectMap<ChangeInfo> myChangeInfos = ContainerUtil.createConcurrentIntObjectMap();
private final IntObjectMap<VfsEventsMerger.ChangeInfo> myChangeInfos = ContainerUtil.createConcurrentIntObjectMap();
private static final short FILE_ADDED = 1;
private static final short FILE_REMOVED = 2;

View File

@@ -27,6 +27,7 @@ import com.intellij.util.concurrency.AtomicFieldUpdater;
import com.intellij.util.containers.ConcurrentBitSet;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.keyFMap.KeyFMap;
import com.intellij.util.text.CharSequenceHashingStrategy;
import gnu.trove.THashSet;
@@ -84,7 +85,7 @@ public class VfsData {
private static final ConcurrentIntObjectMap<Segment> ourSegments = ContainerUtil.createConcurrentIntObjectMap();
private static final ConcurrentBitSet ourInvalidatedIds = new ConcurrentBitSet();
private static TIntHashSet ourDyingIds = new TIntHashSet();
private static final ConcurrentIntObjectMap<VirtualDirectoryImpl> ourChangedParents = ContainerUtil.createConcurrentIntObjectMap();
private static final IntObjectMap<VirtualDirectoryImpl> ourChangedParents = ContainerUtil.createConcurrentIntObjectMap();
static {
ApplicationManager.getApplication().addApplicationListener(new ApplicationAdapter() {

View File

@@ -30,8 +30,8 @@ import com.intellij.openapi.vfs.newvfs.impl.FileNameCache;
import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry;
import com.intellij.util.*;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.io.*;
import com.intellij.util.io.DataOutputStream;
import com.intellij.util.io.storage.*;
@@ -1127,7 +1127,7 @@ public class FSRecords {
// returns (list of id and its parent ids up to and including id of already cached parent, that already cached parent)
@NotNull
static Pair<TIntArrayList, VirtualFileSystemEntry> getParents(int id, @NotNull ConcurrentIntObjectMap<VirtualFileSystemEntry> idCache) {
static Pair<TIntArrayList, VirtualFileSystemEntry> getParents(int id, @NotNull IntObjectMap<VirtualFileSystemEntry> idCache) {
TIntArrayList ids = new TIntArrayList(10);
r.lock();
VirtualFileSystemEntry cached = null;

View File

@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.vfs.newvfs.persistent;
import com.intellij.concurrency.ConcurrentCollectionFactory;
import com.intellij.concurrency.JobSchedulerImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
@@ -41,14 +42,17 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.BitUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.UriUtil;
import com.intellij.concurrency.ConcurrentCollectionFactory;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.io.ReplicatorInputStream;
import com.intellij.util.io.URLUtil;
import com.intellij.util.messages.MessageBus;
import gnu.trove.*;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -67,7 +71,8 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone
private final Map<String, VirtualFileSystemEntry> myRoots =
ConcurrentCollectionFactory.createMap(10, 0.4f, JobSchedulerImpl.CORES_COUNT, FileUtil.PATH_HASHING_STRATEGY);
private final ConcurrentIntObjectMap<VirtualFileSystemEntry> myRootsById = ContainerUtil.createConcurrentIntObjectMap(10, 0.4f, JobSchedulerImpl.CORES_COUNT);
private final IntObjectMap<VirtualFileSystemEntry>
myRootsById = ContainerUtil.createConcurrentIntObjectMap(10, 0.4f, JobSchedulerImpl.CORES_COUNT);
// FS roots must be in this map too. findFileById() relies on this.
private final ConcurrentIntObjectMap<VirtualFileSystemEntry> myIdToDirCache = ContainerUtil.createConcurrentIntObjectSoftValueMap();

View File

@@ -60,6 +60,12 @@ public class ContainerUtilCollectionsTest {
checkClearsEventuallyAfterGCPressure(map, ()->map.put(new Object(), new Object()));
checkClearsEventuallyAfterGCPressure(map, ()->map.put(new Object(), this));
}
private void checkKeyTossedEventually(ObjectIntMap<Object> map) {
checkClearsEventuallyAfterGCPressure(map, ()->map.put(new Object(), 0));
}
private void checkValueTossedEventually(IntObjectMap<Object> map) {
checkClearsEventuallyAfterGCPressure(map, ()->map.put(0, new Object()));
}
private void checkValueTossedEventually(Map<Object, Object> map) {
checkClearsEventuallyAfterGCPressure(map, ()->map.put(new Object(), new Object()));
checkClearsEventuallyAfterGCPressure(map, ()->map.put(this, new Object()));
@@ -172,6 +178,58 @@ public class ContainerUtilCollectionsTest {
assertNull(map.get(strong));
}
private static final int RANDOM_INT = 987654321;
private void checkClearsEventuallyAfterGCPressure(ObjectIntMap<Object> map, @NotNull Runnable put) {
assertTrue(map.isEmpty());
assertEquals(0, map.size());
put.run();
strong = new Object();
//noinspection SizeReplaceableByIsEmpty
do {
map.put(strong, RANDOM_INT); // to run processQueues();
assertFalse(map.isEmpty());
map.remove(strong);
assertEquals(0, map.get(strong));
GCUtil.tryGcSoftlyReachableObjects();
System.gc();
}
while (map.size() != 0);
assertTrue(map.isEmpty());
assertEquals(0, map.size());
map.put(this, RANDOM_INT);
assertEquals(1, map.size());
map.clear();
assertEquals(0, map.size());
assertEquals(0, map.get(strong));
}
private void checkClearsEventuallyAfterGCPressure(IntObjectMap<Object> map, @NotNull Runnable put) {
assertTrue(map.isEmpty());
assertEquals(0, map.size());
put.run();
strong = new Object();
//noinspection SizeReplaceableByIsEmpty
do {
map.put(RANDOM_INT, strong); // to run processQueues();
assertFalse(map.isEmpty());
map.remove(RANDOM_INT);
assertNull(map.get(RANDOM_INT));
GCUtil.tryGcSoftlyReachableObjects();
System.gc();
}
while (map.size() != 0);
assertTrue(map.isEmpty());
assertEquals(0, map.size());
map.put(RANDOM_INT, this);
assertEquals(1, map.size());
map.clear();
assertEquals(0, map.size());
assertNull(map.get(RANDOM_INT));
}
@Test(timeout = TIMEOUT)
public void testSoftMapCustomStrategy() {
Map<String, String> map = ContainerUtil.createSoftMap(IGNORE_CASE_WITH_CRAZY_HASH_STRATEGY);
@@ -312,7 +370,7 @@ public class ContainerUtilCollectionsTest {
@Test(timeout = TIMEOUT)
public void testConcurrentIntObjectHashMap() {
ConcurrentIntObjectMap<Object> map = ContainerUtil.createConcurrentIntObjectMap();
IntObjectMap<Object> map = ContainerUtil.createConcurrentIntObjectMap();
for (int i = 0; i < 1000; i++) {
Object prev = map.put(i, i);
assertNull(prev);
@@ -500,4 +558,15 @@ public class ContainerUtilCollectionsTest {
set.add(this);
assertEquals(1, set.size());
}
@Test(timeout = TIMEOUT)
public void testWeakKeyIntValueMapTossed() {
ObjectIntMap<Object> map = ContainerUtil.createWeakKeyIntValueMap();
checkKeyTossedEventually(map);
}
@Test(timeout = TIMEOUT)
public void testIntKeyWeakValueMapTossed() {
IntObjectMap<Object> map = ContainerUtil.createIntKeyWeakValueMap();
checkValueTossedEventually(map);
}
}

View File

@@ -16,8 +16,8 @@
package com.intellij.openapi.util;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,12 +31,12 @@ import java.util.concurrent.atomic.AtomicInteger;
* @author max
* @author Konstantin Bulenkov
*/
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
public class Key<T> {
private static final AtomicInteger ourKeysCounter = new AtomicInteger();
private final int myIndex = ourKeysCounter.getAndIncrement();
private final String myName; // for debug purposes only
private static final ConcurrentIntObjectMap<Key> allKeys = ContainerUtil.createConcurrentIntObjectWeakValueMap();
private static final IntObjectMap<Key> allKeys = ContainerUtil.createConcurrentIntObjectWeakValueMap();
public Key(@NotNull @NonNls String name) {
myName = name;
@@ -118,7 +118,7 @@ public class Key<T> {
*/
@Nullable
public static Key<?> findKeyByName(String name) {
for (ConcurrentIntObjectMap.IntEntry<Key> key : allKeys.entries()) {
for (IntObjectMap.Entry<Key> key : allKeys.entries()) {
if (name.equals(key.getValue().myName)) {
//noinspection unchecked
return key.getValue();

View File

@@ -127,36 +127,36 @@ abstract class ConcurrentIntKeyRefValueHashMap<V> implements ConcurrentIntObject
@NotNull
@Override
public Iterable<IntEntry<V>> entries() {
final Iterator<IntEntry<IntReference<V>>> entryIterator = myMap.entries().iterator();
return new Iterable<ConcurrentIntObjectMap.IntEntry<V>>() {
public Iterable<Entry<V>> entries() {
final Iterator<Entry<IntReference<V>>> entryIterator = myMap.entries().iterator();
return new Iterable<Entry<V>>() {
@NotNull
@Override
public Iterator<ConcurrentIntObjectMap.IntEntry<V>> iterator() {
return new Iterator<IntEntry<V>>() {
private IntEntry<V> next = nextAliveEntry();
public Iterator<Entry<V>> iterator() {
return new Iterator<Entry<V>>() {
private Entry<V> next = nextAliveEntry();
@Override
public boolean hasNext() {
return next != null;
}
@Override
public IntEntry<V> next() {
public Entry<V> next() {
if (!hasNext()) throw new NoSuchElementException();
IntEntry<V> result = next;
Entry<V> result = next;
next = nextAliveEntry();
return result;
}
private IntEntry<V> nextAliveEntry() {
private Entry<V> nextAliveEntry() {
while (entryIterator.hasNext()) {
IntEntry<IntReference<V>> entry = entryIterator.next();
Entry<IntReference<V>> entry = entryIterator.next();
final V v = entry.getValue().get();
if (v == null) {
continue;
}
final int key = entry.getKey();
return new IntEntry<V>() {
return new Entry<V>() {
@Override
public int getKey() {
return key;
@@ -193,8 +193,8 @@ abstract class ConcurrentIntKeyRefValueHashMap<V> implements ConcurrentIntObject
return myMap.isEmpty();
}
@NotNull
@Override
@NotNull
public Enumeration<V> elements() {
final Enumeration<IntReference<V>> elementRefs = myMap.elements();
return new Enumeration<V>() {

View File

@@ -130,7 +130,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
* are special, and contain null keys and values (but are never
* exported). Otherwise, keys and vals are never null.
*/
static class Node<V> implements IntEntry<V> {
static class Node<V> implements Entry<V> {
final int hash;
final int key;
volatile V val;
@@ -168,9 +168,9 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
public final boolean equals(Object o) {
Object v;
Object u;
IntEntry<?> e;
return ((o instanceof IntEntry) &&
(e = (IntEntry<?>)o).getKey() == key &&
Entry<?> e;
return ((o instanceof Entry) &&
(e = (Entry<?>)o).getKey() == key &&
(v = e.getValue()) != null &&
(v == (u = val) || v.equals(u)));
}
@@ -770,7 +770,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
*
* @return the set view
*/
public Set<IntEntry<V>> entrySet() {
public Set<Entry<V>> entrySet() {
EntrySetView<V> es;
return (es = entrySet) != null ? es : (entrySet = new EntrySetView<V>(this));
}
@@ -846,7 +846,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
if (!(o instanceof ConcurrentIntObjectMap)) {
return false;
}
ConcurrentIntObjectMap<?> m = (ConcurrentIntObjectMap)o;
IntObjectMap<?> m = (IntObjectMap)o;
Node<V>[] t;
int f = (t = table) == null ? 0 : t.length;
Traverser<V> it = new Traverser<V>(t, f, 0, f);
@@ -857,7 +857,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
return false;
}
}
for (IntEntry e : m.entries()) {
for (Entry e : m.entries()) {
int mk = e.getKey();
Object mv;
Object v;
@@ -958,7 +958,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
Object[] entries = new EntrySetView<V>(this).toArray();
int[] result = new int[entries.length];
for (int i = 0; i < entries.length; i++) {
IntEntry<V> entry = (IntEntry<V>)entries[i];
Entry<V> entry = (Entry<V>)entries[i];
result[i] = entry.getKey();
}
return result;
@@ -2396,14 +2396,14 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
}
static final class EntryIterator<V> extends BaseIterator<V>
implements Iterator<IntEntry<V>> {
implements Iterator<Entry<V>> {
EntryIterator(Node<V>[] tab, int index, int size, int limit,
ConcurrentIntObjectHashMap<V> map) {
super(tab, index, size, limit, map);
}
@Override
public final IntEntry<V> next() {
public final Entry<V> next() {
Node<V> p;
if ((p = next) == null) {
throw new NoSuchElementException();
@@ -2412,7 +2412,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
final V v = p.val;
lastReturned = p;
advance();
return new IntEntry<V>() {
return new Entry<V>() {
@Override
public int getKey() {
return k;
@@ -2673,7 +2673,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
@NotNull
@Override
public Iterable<IntEntry<V>> entries() {
public Iterable<Entry<V>> entries() {
return new EntrySetView<V>(this);
}
@@ -2682,8 +2682,8 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
* entries. This class cannot be directly instantiated. See
* {@link #entrySet()}.
*/
static final class EntrySetView<V> extends CollectionView<V, IntEntry<V>>
implements Set<IntEntry<V>> {
static final class EntrySetView<V> extends CollectionView<V, Entry<V>>
implements Set<Entry<V>> {
EntrySetView(ConcurrentIntObjectHashMap<V> map) {
super(map);
@@ -2693,9 +2693,9 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
public boolean contains(Object o) {
Object v;
Object r;
IntEntry<?> e;
return ((o instanceof IntEntry) &&
(r = map.get((e = (IntEntry)o).getKey())) != null &&
Entry<?> e;
return ((o instanceof IntObjectMap.Entry) &&
(r = map.get((e = (Entry)o).getKey())) != null &&
(v = e.getValue()) != null &&
(v == r || v.equals(r)));
}
@@ -2703,9 +2703,9 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
@Override
public boolean remove(Object o) {
Object v;
IntEntry<?> e;
Entry<?> e;
return ((o instanceof Map.Entry) &&
(e = (IntEntry<?>)o) != null &&
(e = (Entry<?>)o) != null &&
(v = e.getValue()) != null &&
map.remove(e.getKey(), v));
}
@@ -2715,7 +2715,7 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
*/
@NotNull
@Override
public Iterator<IntEntry<V>> iterator() {
public Iterator<Entry<V>> iterator() {
ConcurrentIntObjectHashMap<V> m = map;
Node<V>[] t;
int f = (t = m.table) == null ? 0 : t.length;
@@ -2723,14 +2723,14 @@ class ConcurrentIntObjectHashMap<V> implements ConcurrentIntObjectMap<V> {
}
@Override
public boolean add(IntEntry<V> e) {
public boolean add(Entry<V> e) {
return map.putVal(e.getKey(), e.getValue(), false) == null;
}
@Override
public boolean addAll(Collection<? extends IntEntry<V>> c) {
public boolean addAll(Collection<? extends Entry<V>> c) {
boolean added = false;
for (IntEntry<V> e : c) {
for (Entry<V> e : c) {
if (add(e)) {
added = true;
}

View File

@@ -17,7 +17,6 @@ package com.intellij.util.containers;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Enumeration;
/**
@@ -27,7 +26,7 @@ import java.util.Enumeration;
* Methods are adapted from {@link java.util.concurrent.ConcurrentMap} to integer keys
* @see java.util.concurrent.ConcurrentMap
*/
public interface ConcurrentIntObjectMap<V> {
public interface ConcurrentIntObjectMap<V> extends IntObjectMap<V> {
/**
* @return written value
*/
@@ -36,17 +35,8 @@ public interface ConcurrentIntObjectMap<V> {
boolean remove(int key, @NotNull V value);
boolean replace(int key, @NotNull V oldValue, @NotNull V newValue);
// regular Map methods
V put(int key, @NotNull V value);
V get(int key);
V remove(int key);
boolean containsKey(int key);
void clear();
@NotNull
Iterable<IntEntry<V>> entries();
@NotNull
int[] keys();
Enumeration<V> elements();
/**
* @return Approximate number of elements in the map.
@@ -55,23 +45,13 @@ public interface ConcurrentIntObjectMap<V> {
* and Second, for weak- or soft- keyed maps it returns the total number of references
* rather than alive values because otherwise it would be too expensive
*/
@Override
int size();
boolean isEmpty();
@NotNull
Enumeration<V> elements();
@NotNull
Collection<V> values();
boolean containsValue(@NotNull V value);
/**
* @return the previous value associated with the specified key,
* or {@code null} if there was no mapping for the key
*/
V putIfAbsent(int key, @NotNull V value);
interface IntEntry<V> {
int getKey();
@NotNull
V getValue();
}
}

View File

@@ -2855,5 +2855,14 @@ public class ContainerUtil extends ContainerUtilRt {
public static <T> Set<T> createWeakSet() {
return new WeakHashSet<T>();
}
@NotNull
public static <T> IntObjectMap<T> createIntKeyWeakValueMap() {
return new IntKeyWeakValueHashMap<T>();
}
@NotNull
public static <T> ObjectIntMap<T> createWeakKeyIntValueMap() {
return new WeakKeyIntValueHashMap<T>();
}
}

View File

@@ -15,17 +15,22 @@
*/
package com.intellij.util.containers;
import com.intellij.openapi.util.Condition;
import com.intellij.reference.SoftReference;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TIntObjectIterator;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
class WeakValueIntObjectHashMap<V> {
class IntKeyWeakValueHashMap<V> implements IntObjectMap<V> {
private final TIntObjectHashMap<MyReference<V>> myMap = new TIntObjectHashMap<MyReference<V>>();
private final ReferenceQueue<V> myQueue = new ReferenceQueue<V>();
@@ -47,19 +52,16 @@ class WeakValueIntObjectHashMap<V> {
}
int key = ref.key;
myMap.remove(key);
keyExpired(key);
}
}
protected void keyExpired(int key) {
}
@Override
public final V get(int key) {
MyReference<V> ref = myMap.get(key);
return SoftReference.dereference(ref);
}
@Override
public final V put(int key, @NotNull V value) {
processQueue();
MyReference<V> ref = new MyReference<V>(key, value, myQueue);
@@ -68,29 +70,35 @@ class WeakValueIntObjectHashMap<V> {
return SoftReference.dereference(oldRef);
}
@Override
public final V remove(int key) {
processQueue();
MyReference<V> ref = myMap.remove(key);
return SoftReference.dereference(ref);
}
@Override
public final void clear() {
myMap.clear();
processQueue();
}
@Override
public final int size() {
return myMap.size();
}
@Override
public final boolean isEmpty() {
return myMap.isEmpty();
}
@Override
public final boolean containsKey(int key) {
throw RefValueHashMap.pointlessContainsKey();
}
@Override
@NotNull
public final Collection<V> values() {
List<V> result = new ArrayList<V>();
@@ -104,4 +112,63 @@ class WeakValueIntObjectHashMap<V> {
}
return result;
}
@NotNull
@Override
public int[] keys() {
throw new IncorrectOperationException("keys() makes no sense for weak/soft map because GC can clear the value any moment now");
}
@Override
public boolean containsValue(@NotNull V value) {
return values().contains(value);
}
private static final Object GCED = new Object();
@NotNull
@Override
public Iterable<Entry<V>> entries() {
return new Iterable<Entry<V>>() {
@NotNull
@Override
public Iterator<Entry<V>> iterator() {
final TIntObjectIterator<MyReference<V>> tIterator = myMap.iterator();
return ContainerUtil.filterIterator(new Iterator<Entry<V>>() {
@Override
public boolean hasNext() {
return tIterator.hasNext();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public Entry<V> next() {
tIterator.advance();
return new Entry<V>() {
@Override
public int getKey() {
return tIterator.key();
}
@NotNull
@Override
public V getValue() {
V v = SoftReference.dereference(tIterator.value());
//noinspection unchecked
return ObjectUtils.notNull(v, (V)GCED);
}
};
}
}, new Condition<Entry<V>>() {
@Override
public boolean value(Entry<V> o) {
return o.getValue() != GCED;
}
});
}
};
}
}

View File

@@ -0,0 +1,51 @@
// Copyright 2000-2017 JetBrains s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.intellij.util.containers;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public interface IntObjectMap<V> {
V put(int key, @NotNull V value);
V get(int key);
V remove(int key);
boolean containsKey(int key);
void clear();
@NotNull
int[] keys();
int size();
boolean isEmpty();
@NotNull
Collection<V> values();
boolean containsValue(@NotNull V value);
interface Entry<V> {
int getKey();
@NotNull
V getValue();
}
@NotNull
Iterable<Entry<V>> entries();
}

View File

@@ -0,0 +1,52 @@
// Copyright 2000-2017 JetBrains s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.intellij.util.containers;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
public interface ObjectIntMap<K> {
int get(@NotNull K key);
int put(@NotNull K key, int value);
int remove(@NotNull K key);
boolean containsKey(@NotNull K key);
void clear();
@NotNull
Set<K> keySet();
int size();
boolean isEmpty();
@NotNull
int[] values();
boolean containsValue(int value);
interface Entry<K> {
@NotNull
K getKey();
int getValue();
}
@NotNull
Iterable<Entry<K>> entries();
}

View File

@@ -0,0 +1,180 @@
// Copyright 2000-2017 JetBrains s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.intellij.util.containers;
import com.intellij.openapi.util.Condition;
import com.intellij.reference.SoftReference;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import gnu.trove.THashSet;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntIterator;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.Set;
class WeakKeyIntValueHashMap<K> implements ObjectIntMap<K> {
private final TObjectIntHashMap<MyReference<K>> myMap = new TObjectIntHashMap<MyReference<K>>();
private final ReferenceQueue<K> myQueue = new ReferenceQueue<K>();
private static class MyReference<T> extends WeakReference<T> {
private final int myHashCode;
private MyReference(@NotNull T key, ReferenceQueue<? super T> q) {
super(key, q);
myHashCode = key.hashCode();
}
// when key is GC-ed, equality should be identity-based
@Override
public boolean equals(Object obj) {
if (!(obj instanceof MyReference)) return false;
MyReference<T> other = (MyReference)obj;
T myKey = get();
T otherKey = other.get();
return obj == this || myKey != null && otherKey != null && myKey.equals(otherKey);
}
@Override
public int hashCode() {
return myHashCode;
}
}
private void processQueue() {
while(true){
MyReference<K> ref = (MyReference)myQueue.poll();
if (ref == null) {
return;
}
myMap.remove(ref);
}
}
@Override
public final int get(@NotNull K key) {
MyReference<K> ref = new MyReference<K>(key, null);
return myMap.get(ref);
}
@Override
public final int put(@NotNull K key, int value) {
processQueue();
MyReference<K> ref = new MyReference<K>(key, myQueue);
return myMap.put(ref, value);
}
@Override
public final int remove(@NotNull K key) {
processQueue();
MyReference<K> ref = new MyReference<K>(key, myQueue);
return myMap.remove(ref);
}
@Override
public final void clear() {
myMap.clear();
processQueue();
}
@Override
public final int size() {
return myMap.size();
}
@Override
public final boolean isEmpty() {
return myMap.isEmpty();
}
@Override
public final boolean containsKey(@NotNull K key) {
MyReference<K> ref = new MyReference<K>(key, null);
return myMap.containsKey(ref);
}
@Override
@NotNull
public final int[] values() {
throw new IncorrectOperationException("values() makes no sense for weak/soft key map because GC can clear the key any moment now");
}
@NotNull
@Override
public Set<K> keySet() {
return new THashSet<K>(ContainerUtil.map(myMap.keys(), new Function<Object, K>() {
@Override
public K fun(Object ref) {
return SoftReference.dereference((MyReference<K>)ref);
}
}));
}
@Override
public boolean containsValue(int value) {
throw RefValueHashMap.pointlessContainsValue();
}
private static final Object GCED = new Object();
@NotNull
@Override
public Iterable<Entry<K>> entries() {
return new Iterable<Entry<K>>() {
@NotNull
@Override
public Iterator<Entry<K>> iterator() {
final TObjectIntIterator<MyReference<K>> tIterator = myMap.iterator();
return ContainerUtil.filterIterator(new Iterator<Entry<K>>() {
@Override
public boolean hasNext() {
return tIterator.hasNext();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public Entry<K> next() {
tIterator.advance();
return new Entry<K>() {
@NotNull
@Override
public K getKey() {
K v = SoftReference.dereference(tIterator.key());
//noinspection unchecked
return ObjectUtils.notNull(v, (K)GCED);
}
@Override
public int getValue() {
return tIterator.value();
}
};
}
}, new Condition<Entry<K>>() {
@Override
public boolean value(Entry<K> o) {
return o.getKey() != GCED;
}
});
}
};
}
}

View File

@@ -17,8 +17,8 @@ package com.intellij.vcs.log.data;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntObjectMap;
import com.intellij.vcs.log.VcsCommitMetadata;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -28,7 +28,7 @@ import java.util.List;
public class TopCommitsCache {
@NotNull private final VcsLogStorage myStorage;
@NotNull private final ConcurrentIntObjectMap<VcsCommitMetadata> myCache = ContainerUtil.createConcurrentIntObjectMap();
@NotNull private final IntObjectMap<VcsCommitMetadata> myCache = ContainerUtil.createConcurrentIntObjectMap();
@NotNull private List<VcsCommitMetadata> mySortedDetails = ContainerUtil.newArrayList();
public TopCommitsCache(@NotNull VcsLogStorage storage) {