You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@netbeans.apache.org by GitBox <gi...@apache.org> on 2017/11/05 12:45:19 UTC

[GitHub] svatoun commented on a change in pull request #161: [NETBEANS-96] New PAC Script evaluation environment

svatoun commented on a change in pull request #161: [NETBEANS-96] New PAC Script evaluation environment
URL: https://github.com/apache/incubator-netbeans/pull/161#discussion_r148956352
 
 

 ##########
 File path: core.network/src/org/netbeans/core/network/utils/SimpleObjCache.java
 ##########
 @@ -0,0 +1,136 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.netbeans.core.network.utils;
+
+import java.util.Comparator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Simple cache with a max capacity. Encapsulates {@link java.util.Map Map}.
+ *
+ * <p>
+ * Eviction is based on usage. Every time a value is retrieved from the cache
+ * its 'usage timestamp' will change. When the cache is full, the value with the
+ * oldest usage timestamp will be evicted.
+ *
+ * <p>
+ * This class is thread-safe and is very efficient for read operations
+ * ({@code get}). The write operation ({@code put}) will - if the cache is full
+ * - have performance which degrades linearly with the size of the cache as all
+ * elements will have to be inspected to find the eviction candidate. For this
+ * reason, the class is best suited for smaller caches (say less than 1000
+ * elements).
+ *
+ *
+ * @author lbruun
+ * 
+ * @param <K> key 
+ * @param <V> value
+ */
+public class SimpleObjCache<K,V> {
+
+    private final int maxSize;
+    private final ConcurrentHashMap<K, ValueWrapper> map;
+    
+    /**
+     * Constructs a new cache with {@code maxSize} capacity. When the 
+     * cache is full, the value used/created the furthest in the past will
+     * be evicted to make room for a new element.
+     * 
+     * @param maxSize capacity - any value larger than zero
+     */
+    public SimpleObjCache(int maxSize) {
+        
+        if (maxSize <= 0) {
+            throw new IllegalArgumentException("Cache max size cannot be zero or less");
+        }
+        this.maxSize = maxSize;
+        this.map = new ConcurrentHashMap<>();
+    }
+    
+    /**
+     * Puts a value into the cache mapped to the specified key.
+     * 
+     * @param key key with which the specified value is to be associated
+     * @param value value to be associated with the specified key
+     * @return the previous value associated with key, or null if there 
+     *         was no mapping for the key
+     */
+    public V put(K key, V value) {
+        if (map.size() >= maxSize) {
+            map.remove(findEvictionCandidate());
+        }
+        ValueWrapper existingValue = map.put(key, new ValueWrapper(value));
+        if (existingValue != null) {
+            return existingValue.value;
+        } else {
+            return null;
+        }
+    }
+    
+    /**
+     * Returns the value to which the specified key is mapped, or {@code null}
+     * if this cache contains no mapping for the key.
+     * 
+     * @param key the key whose associated value is to be returned
+     * @return  the value to which the specified key is mapped, or {@code null}
+     *          if this map contains no mapping for the key
+     */
+    public V get(K key) {
+        ValueWrapper wrapper = map.get(key);
+        if (wrapper != null) {
+            wrapper.lastUsed.set(System.currentTimeMillis());
+            return wrapper.value;
+        }
+        return null;
+    }
+    
+    /**
+     * Gets the current number of elements in the cache.
+     * @return 
+     */
+    public int getCacheSize() {
+        return map.size();
+    }
+    
+    /**
+     * Removes all elements from the cache. There is little reason to use
+     * this method as the cache will do its own house keeping.
+     */
+    public void clear() {
+        map.clear();
+    }
+    
+    private K findEvictionCandidate() {
+        // Finds minimum of all timestamps of all elements in the cache.
+        return map.entrySet().stream()
+                .min(Comparator.comparingLong( e -> e.getValue().lastUsed.get()))
+                .get().getKey();
 
 Review comment:
   concurrentMap.entrySet() is not a copy, it is still backed by the map. So any parallel remove from the origin (concurrent)Map will be reflected in the Set, therefore invalidating Iterators and Streams derived from the entry set. The stream will eventually give a ConcurrentModificationException. 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services