此答案由Jon Vint
基於註釋它看起來更像你需要一個番石榴表
番石榴表看起來像它給你你需要什麼,但沒有目前是一個線程安全實現。部分難點在於您需要管理地圖的地圖,並公開對值的地圖的訪問。
但是如果你很樂意同步訪問你自己的集合,我認爲一個番石榴表可以爲你提供你想要的功能,並且你可以添加線程安全。並添加你想要的公用事業公司的長期效用。
這一點比你問的比較抽象,但我認爲這給你需要的東西:
import com.google.common.base.MoreObjects;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Table;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.concurrent.GuardedBy;
/**
* Provide something like the {@link com.google.common.util.concurrent.AtomicLongMap} but supporting
* multiple keys.
*
* Should be able to put a value using two keys. And retrieve either a precise cell. Or retrieve a
* collection of values.
*
* Created by James on 28/02/2017.
*/
public class SynchronizedMultimap<Row, Column, Value> {
private final Object mutex = new Object();
@GuardedBy("mutex") // All read and write access to delegate must be protected by mutex.
private final Table<Row, Column, Value> delegate = HashBasedTable.create();
/**
* {@link Table#put(Object, Object, Object)}
* Associates the specified value with the specified keys. If the table
* already contained a mapping for those keys, the old value is replaced with
* the specified value.
*
* @return The old value associated with the keys or {@code null} if no previous value existed.
*/
public Value put(Row row, Column column, Value value) {
synchronized (mutex) {
return delegate.put(row, column, value);
}
}
/**
* {@link java.util.concurrent.ConcurrentMap#computeIfAbsent(Object, Function)}
*
* Checks the existing value in the table delegate by {@link Table#get(Object, Object)} and
* applies the given function, the function in this example should be able to handle a null input.
*
* @return The current value of the Table for keys, whether the function is applied or not.
*/
public Value compute(Row row, Column column, Function<Value, Value> function) {
synchronized (mutex) {
Value oldValue = delegate.get(row, column);
Value newValue = function.apply(oldValue);
if (newValue != null) {
delegate.put(row, column, newValue);
return newValue;
}
return oldValue;
}
}
/**
* {@link Table#get(Object, Object)}
*
* @return The value associated with the keys or {@code null} if no value.
*/
public Value get(Row row, Column column) {
synchronized (mutex) {
return delegate.get(row, column);
}
}
/**
* {@link Table#row(Object)}
*
* @return An immutable map view of the columns in the table.
*/
public Map<Column, Value> get(Row row) {
synchronized (mutex) {
// Since we are exposing
return ImmutableMap.copyOf(delegate.row(row));
}
}
@Override
public String toString() {
// Even toString needs protection.
synchronized (mutex) {
return MoreObjects.toStringHelper(this)
.add("delegate", delegate)
.toString();
}
}
}
更長久特定行爲:
/**
* Provides support for similar behaviour as AtomicLongMap.
*
* Created by James on 28/02/2017.
*/
public class SynchronizedLongMultimap<Row, Column> extends SynchronizedMultimap<Row, Column, Long> {
/**
* @return Adds delta to the current value and returns the new value. Or delta if no previous value.
*/
public long addAndGet(Row row, Column column, long delta) {
return compute(row, column,
(Long oldValue) -> (oldValue == null) ? delta : oldValue + delta);
}
/**
* @return Increments the current value and returns the new value. Or 1 if no previous value.
*/
public long increment(Row row, Column column) {
return compute(row, column, (Long oldValue) -> (oldValue == null) ? 1 : oldValue + 1);
}
/**
* @return Decrements the current value and returns the new value. Or -1 if no previous value.
*/
public long decrement(Row row, Column column) {
return compute(row, column, (Long oldValue) -> (oldValue == null) ? -1 : oldValue - 1);
}
}
添加單元測試,顯示邏輯
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import com.google.common.collect.ImmutableMap;
import org.junit.Test;
/**
* Test simple functionality of the Map is sound.
*
* Created by James on 28/02/2017.
*/
public class SynchronizedLongMultimapTest {
private final SynchronizedLongMultimap<String, String> map = new SynchronizedLongMultimap<>();
@Test
public void addAndGet_SingleCell() {
// add and get sets the initial value to the delta
assertThat(map.addAndGet("0", "0", 1), equalTo(1L));
assertThat(map.addAndGet("0", "0", 1), equalTo(2L));
assertThat(map.addAndGet("0", "0", 0), equalTo(2L));
assertThat(map.addAndGet("0", "0", -2), equalTo(0L));
}
@Test
public void addAndGet_RangeCells() {
// add and get sets the initial value to the delta
assertThat(map.addAndGet("0", "1", 123), equalTo(123L));
// add and get sets the initial value to the delta
assertThat(map.addAndGet("1", "1", 42), equalTo(42L));
// add and get adds the delta to the existing value
assertThat(map.addAndGet("1", "1", -42), equalTo(0L));
}
@Test
public void increment() {
// increment sets the initial value to one
assertThat(map.increment("0", "0"), equalTo(1L));
// then adds one each time it's called
assertThat(map.increment("0", "0"), equalTo(2L));
}
@Test
public void decrement(){
// decrement sets the initial value to -1 if no previous value
assertThat(map.decrement("apples", "bananas"), equalTo(-1L));
// then decrements that
assertThat(map.decrement("apples", "bananas"), equalTo(-2L));
}
@Test
public void get_PreviousValueIsNull() {
assertThat(map.get("toast", "bananas"), equalTo(null));
// even if we ask again
assertThat(map.get("toast", "bananas"), equalTo(null));
}
@Test
public void get_ProvidedByPut() {
assertThat(map.put("toast", "corn flakes", 17L), equalTo(null));
// then we get what we put in
assertThat(map.get("toast", "corn flakes"), equalTo(17L));
}
@Test
public void get_ColumnMap() {
// Expected behaviour from MultiKeyMap question
assertThat(map.put("a", "A", 1L), equalTo(null));
assertThat(map.put("a", "B", 2L), equalTo(null));
assertThat(map.put("b", "C", 3L), equalTo(null));
// then we can get a single value
assertThat(map.get("a", "A"), equalTo(1L));
// or a Map
assertThat(map.get("a"), equalTo(ImmutableMap.of("A", 1L, "B", 2L)));
// even if that Map only has a single value
assertThat(map.get("b"), equalTo(ImmutableMap.of("C", 3L)));
}
}
它看起來更像是你需要一個番石榴'表<字符串,字符串,AtomicLong>',不是? –
@ControlAltDel它不會輕易檢索到一個單一的鍵(因爲OP是第二個'get'要求) –
@JohnVint我認爲你的第一條評論是一個答案... – Eugene