forked from plusone/plusone-commons
Compare commits
5 Commits
1e1c003751
...
ebacc622da
Author | SHA1 | Date | |
---|---|---|---|
ebacc622da | |||
9300f02a51 | |||
ab82cfeea6 | |||
2cd0640a0b | |||
cedf77d33c |
@@ -26,9 +26,8 @@ xyz.zhouxy.plusone.commons
|
|||||||
│
|
│
|
||||||
├───collection
|
├───collection
|
||||||
│ AbstractMapWrapper.java [ ]
|
│ AbstractMapWrapper.java [ ]
|
||||||
│ CollectionTools.java [ ]
|
│ CollectionTools.java [Y]
|
||||||
│ MapWrapper.java [ ]
|
│ MapWrapper.java [ ]
|
||||||
│ ReadWriteLockedTable.java [ ]
|
|
||||||
│ SafeConcurrentHashMap.java [ ]
|
│ SafeConcurrentHashMap.java [ ]
|
||||||
│
|
│
|
||||||
├───constant
|
├───constant
|
||||||
|
@@ -27,12 +27,26 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
import xyz.zhouxy.plusone.commons.util.AssertTools;
|
||||||
import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools;
|
import xyz.zhouxy.plusone.commons.util.ConcurrentHashMapTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbstractMapWrapper
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 包装了一个 {@link Map}。
|
||||||
|
* 主要在于获取值时,如果 Key 不存在,是抛出异常;如果 Key 存在,则将 value 包装在 {@link Optional} 中 返回。
|
||||||
|
* 此外可以定义 Key 和 Value 的检查规则。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V, T>> {
|
public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V, T>> {
|
||||||
|
|
||||||
@@ -40,7 +54,9 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
|
|||||||
private final Consumer<K> keyChecker;
|
private final Consumer<K> keyChecker;
|
||||||
private final Consumer<V> valueChecker;
|
private final Consumer<V> valueChecker;
|
||||||
|
|
||||||
protected AbstractMapWrapper(Map<K, V> map, @Nullable Consumer<K> keyChecker, @Nullable Consumer<V> valueChecker) {
|
protected AbstractMapWrapper(Map<K, V> map,
|
||||||
|
@Nullable Consumer<K> keyChecker,
|
||||||
|
@Nullable Consumer<V> valueChecker) {
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.keyChecker = keyChecker;
|
this.keyChecker = keyChecker;
|
||||||
this.valueChecker = valueChecker;
|
this.valueChecker = valueChecker;
|
||||||
@@ -71,10 +87,9 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
|
|||||||
* @return 可缺失的值
|
* @return 可缺失的值
|
||||||
* @throws IllegalArgumentException key 不存在时抛出。
|
* @throws IllegalArgumentException key 不存在时抛出。
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public Optional<V> get(K key) {
|
public Optional<V> get(K key) {
|
||||||
if (!this.map.containsKey(key)) {
|
AssertTools.checkArgument(this.map.containsKey(key), "Key does not exist");
|
||||||
throw new IllegalArgumentException("Key does not exist");
|
|
||||||
}
|
|
||||||
return Optional.ofNullable(this.map.get(key));
|
return Optional.ofNullable(this.map.get(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,8 +195,7 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
|
|||||||
return false;
|
return false;
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass())
|
||||||
return false;
|
return false;
|
||||||
@SuppressWarnings("rawtypes")
|
AbstractMapWrapper<?, ?, ?> other = (AbstractMapWrapper<?, ?, ?>) obj;
|
||||||
AbstractMapWrapper other = (AbstractMapWrapper) obj;
|
|
||||||
return Objects.equals(map, other.map);
|
return Objects.equals(map, other.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,8 +21,16 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 集合工具类
|
||||||
|
*
|
||||||
|
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
|
||||||
|
* @since 0.1.0
|
||||||
|
*/
|
||||||
public class CollectionTools {
|
public class CollectionTools {
|
||||||
|
|
||||||
|
// TODO [添加] 新增其它集合类型,如 guava 的扩展集合等
|
||||||
|
|
||||||
// isEmpty
|
// isEmpty
|
||||||
|
|
||||||
public static boolean isEmpty(@Nullable Collection<?> collection) {
|
public static boolean isEmpty(@Nullable Collection<?> collection) {
|
||||||
|
@@ -26,6 +26,16 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
import com.google.common.annotations.Beta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MapWrapper
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Map 包装器的默认实现
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
|
||||||
|
* @since 0.1.0
|
||||||
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
public final class MapWrapper<K, V> extends AbstractMapWrapper<K, V, MapWrapper<K, V>> {
|
public final class MapWrapper<K, V> extends AbstractMapWrapper<K, V, MapWrapper<K, V>> {
|
||||||
|
|
||||||
|
@@ -1,297 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023-2024 the original author or authors.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* https://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 xyz.zhouxy.plusone.commons.collection;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
import com.google.common.collect.ImmutableTable;
|
|
||||||
import com.google.common.collect.Table;
|
|
||||||
import xyz.zhouxy.plusone.commons.annotation.ReaderMethod;
|
|
||||||
import xyz.zhouxy.plusone.commons.annotation.WriterMethod;
|
|
||||||
|
|
||||||
import javax.annotation.CheckForNull;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 {@link ReentrantReadWriteLock} 将 {@link Table} 包装为线程安全的集合。
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 可通过以下方式构建一个线程安全的 {@link Table}
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* LockedTable.of(HashBasedTable.create())
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* <b>NOTE: 如果 {@link Table} 不需要更改,请使用 {@link ImmutableTable}</b>
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
|
|
||||||
* @see Table
|
|
||||||
* @see ImmutableTable
|
|
||||||
* @see ReentrantReadWriteLock
|
|
||||||
* @since 0.1.0-SNAPSHOT
|
|
||||||
*/
|
|
||||||
@Beta
|
|
||||||
@ThreadSafe
|
|
||||||
public class ReadWriteLockedTable<R, C, V> implements Table<R, C, V> {
|
|
||||||
|
|
||||||
private final Table<R, C, V> table;
|
|
||||||
|
|
||||||
private final ReentrantReadWriteLock.ReadLock readLock;
|
|
||||||
private final ReentrantReadWriteLock.WriteLock writeLock;
|
|
||||||
|
|
||||||
private ReadWriteLockedTable(Table<R, C, V> table, boolean fair) {
|
|
||||||
this.table = Objects.requireNonNull(table);
|
|
||||||
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(fair);
|
|
||||||
this.readLock = rwl.readLock();
|
|
||||||
this.writeLock = rwl.writeLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <R, C, V> ReadWriteLockedTable<R, C, V> of(Table<R, C, V> table) {
|
|
||||||
if (table instanceof ReadWriteLockedTable) {
|
|
||||||
return (ReadWriteLockedTable<R, C, V>) table;
|
|
||||||
} else {
|
|
||||||
return new ReadWriteLockedTable<>(table, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <R, C, V> ReadWriteLockedTable<R, C, V> of(Table<R, C, V> table, boolean fair) {
|
|
||||||
if (table instanceof ReadWriteLockedTable) {
|
|
||||||
return (ReadWriteLockedTable<R, C, V>) table;
|
|
||||||
} else {
|
|
||||||
return new ReadWriteLockedTable<>(table, fair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public boolean contains(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.contains(rowKey, columnKey);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public boolean containsRow(@CheckForNull @Nonnull Object rowKey) {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.containsRow(rowKey);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public boolean containsColumn(@CheckForNull @Nonnull Object columnKey) {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.containsColumn(columnKey);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public boolean containsValue(@CheckForNull @Nonnull Object value) {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.containsValue(value);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public V get(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.get(rowKey, columnKey);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public boolean isEmpty() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.isEmpty();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public int size() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.size();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WriterMethod
|
|
||||||
public void clear() {
|
|
||||||
writeLock.lock();
|
|
||||||
try {
|
|
||||||
this.table.clear();
|
|
||||||
} finally {
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WriterMethod
|
|
||||||
public V put(@CheckForNull @Nonnull R rowKey,
|
|
||||||
@CheckForNull @Nonnull C columnKey,
|
|
||||||
@CheckForNull @Nonnull V value) {
|
|
||||||
writeLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.put(rowKey, columnKey, value);
|
|
||||||
} finally {
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WriterMethod
|
|
||||||
public void putAll(@Nonnull Table<? extends R, ? extends C, ? extends V> table) {
|
|
||||||
writeLock.lock();
|
|
||||||
try {
|
|
||||||
this.table.putAll(table);
|
|
||||||
} finally {
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WriterMethod
|
|
||||||
public V remove(@CheckForNull @Nonnull Object rowKey, @CheckForNull @Nonnull Object columnKey) {
|
|
||||||
writeLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.remove(rowKey, columnKey);
|
|
||||||
} finally {
|
|
||||||
writeLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Map<C, V> row(@CheckForNull @Nonnull R rowKey) {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.row(rowKey);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Map<R, V> column(@CheckForNull @Nonnull C columnKey) {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.column(columnKey);
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Set<Cell<R, C, V>> cellSet() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.cellSet();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Set<R> rowKeySet() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.rowKeySet();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Set<C> columnKeySet() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.columnKeySet();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Collection<V> values() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.values();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Map<R, Map<C, V>> rowMap() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.rowMap();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@ReaderMethod
|
|
||||||
public Map<C, Map<R, V>> columnMap() {
|
|
||||||
readLock.lock();
|
|
||||||
try {
|
|
||||||
return this.table.columnMap();
|
|
||||||
} finally {
|
|
||||||
readLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -115,7 +115,7 @@ public enum Quarter {
|
|||||||
|
|
||||||
// StaticFactoryMethods end
|
// StaticFactoryMethods end
|
||||||
|
|
||||||
// computs
|
// computes
|
||||||
|
|
||||||
public Quarter plus(long quarters) { // TODO 单元测试
|
public Quarter plus(long quarters) { // TODO 单元测试
|
||||||
final int amount = (int) ((quarters % 4) + 4);
|
final int amount = (int) ((quarters % 4) + 4);
|
||||||
@@ -126,7 +126,7 @@ public enum Quarter {
|
|||||||
return plus(-(quarters % 4));
|
return plus(-(quarters % 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
// computs end
|
// computes end
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
|
@@ -0,0 +1,33 @@
|
|||||||
|
package xyz.zhouxy.plusone.commons.collection;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class CollectionToolsTests {
|
||||||
|
@Test
|
||||||
|
void testIsEmpty() {
|
||||||
|
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
assertTrue(CollectionTools.isEmpty(list));
|
||||||
|
assertFalse(CollectionTools.isNotEmpty(list));
|
||||||
|
|
||||||
|
list.add("Test");
|
||||||
|
assertFalse(CollectionTools.isEmpty(list));
|
||||||
|
assertTrue(CollectionTools.isNotEmpty(list));
|
||||||
|
|
||||||
|
Map<String, Integer> map = new HashMap<>();
|
||||||
|
assertTrue(CollectionTools.isEmpty(map));
|
||||||
|
assertFalse(CollectionTools.isNotEmpty(map));
|
||||||
|
|
||||||
|
map.put("2", 2);
|
||||||
|
assertFalse(CollectionTools.isEmpty(map));
|
||||||
|
assertTrue(CollectionTools.isNotEmpty(map));
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user