Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2014 Zach Melamed
   * 
   * Latest version available online at https://github.com/zach-m/tectonica-commons
   *
   * 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.tectonica.collections;
 
Concurrent, thread-safe container, retaining a key-value map with a reference count mechanism. Instead of the standard get, put and remove methods, if offers the following:
  • acquire - gets a value given a key, creating it if necessary. If the key already exists, its reference count is increased
  • release - decreases a reference count of a value, removing it if the number of acquires matches the number of releases
of course, both actions are performed atomically.

When the class is constructed a default AutoEvictMap.Factory may be provided, which will generate the values when acquire(java.lang.Object) needs them. Alternatively, on each invocation of acquire(java.lang.Object,com.tectonica.collections.AutoEvictMap.Factory), a custom ad-hoc factory may be provided for the particular key acquired. When keys are released, no additional action is taken.

Author(s):
Zach Melamed
 
 public class AutoEvictMap<K, V>
 {
 	public static interface Factory<K, V>
 	{
 		V valueOf(K key);
 	}
 
 	private final ConcurrentMap<K, Holder<K, V>> map = new ConcurrentHashMap<K, Holder<K, V>>();
 	private final Factory<K, V> defaultFactory;
 
 	public AutoEvictMap()
 	{
 		this. = null;
 	}
 
 	public AutoEvictMap(Factory<K, V> defaultFactory)
 	{
 		this. = defaultFactory;
 	}
 
 	public V acquire(final K keythrows InterruptedException
 	{
 		if ( == null)
 			throw new NullPointerException("defaultFactory");
 
 		return acquire(key);
 	}
 
 	public V acquire(final K keyfinal Factory<K, V> customFactorythrows InterruptedException
 	{
 		if (key == null)
 			throw new NullPointerException("key");
 		if (customFactory == null)
 			throw new NullPointerException("customFactory");
 
 		Holder<K, V> holder;
 		while (true)
 		{
 			holder = .get(key);
 			if (holder == null)
 			{
 				if (.putIfAbsent(keyholder = new Holder<K, V>(keycustomFactory)) == null)
 				{
 					// initial creation of the value
 					holder.run();
 					break;
 				}
 			}
 			else
 			{
 				if (.replace(keyholderholder = holder.inc()))
 					break// ref-count increased
 			}
 		}
 
 		return holder.get(); // NOTE: think whether to remove from map in case of exception/cancellation
 	}
	public boolean release(K key)
	{
		if (key == null)
			throw new NullPointerException("key");
		while (true)
		{
			Holder<K, V> holder = .get(key);
			if (holder == null)
				return true// was already removed
			if (holder.isInitial())
			{
				if (.remove(keyholder.initial()))
					return true// removed now
			}
			else
			{
				if (.replace(keyholderholder.dec()))
					return false// not removed, just decreased ref-count
			}
		}
	}
	public int size()
	{
		return .size();
	}
	public void clear()
	{
	}
	// /////////////////////////////////////////////////////////////////////////////////////////
	private static class Holder<K, V>
	{
		private final FutureTask<V> ft;
		private final int refCount;
		public Holder(final K keyfinal Factory<K, V> generator)
		{
			 = new FutureTask<V>(new Callable<V>()
			{
				public V call() throws InterruptedException
				{
					return generator.valueOf(key);
				}
			});
			 = 1;
		}
		private Holder(FutureTask<V> ftint refCount)
		{
			this. = ft;
			this. = refCount;
		}
		public V get() throws InterruptedException
		{
			try
			{
				return .get();
			}
			{
				Throwable t = e.getCause();
				if (t instanceof RuntimeException)
					throw (RuntimeExceptiont;
				else if (t instanceof Error)
					throw (Errort;
				else
					throw new IllegalStateException("Not unchecked"t);
			}
		}
		public void run()
		{
		}
		public Holder<K, V> inc()
		{
			return new Holder<K, V>( + 1);
		}
		public Holder<K, V> dec()
		{
			return new Holder<K, V>( - 1);
		}
		public Holder<K, V> initial()
		{
			return new Holder<K, V>(, 1);
		}
		public boolean isInitial()
		{
			return  == 1;
		}
		@SuppressWarnings("unchecked")
		public boolean equals(Object obj)
		{
			return ( == ((Holder<K, V>) obj).);
		}
	}
New to GrepCode? Check out our FAQ X