5 Commits

7 changed files with 74 additions and 307 deletions

View File

@@ -26,9 +26,8 @@ xyz.zhouxy.plusone.commons
├───collection
│ AbstractMapWrapper.java [ ]
│ CollectionTools.java [ ]
│ CollectionTools.java [Y]
│ MapWrapper.java [ ]
│ ReadWriteLockedTable.java [ ]
│ SafeConcurrentHashMap.java [ ]
├───constant

View File

@@ -27,12 +27,26 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
import xyz.zhouxy.plusone.commons.util.AssertTools;
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
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<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.keyChecker = keyChecker;
this.valueChecker = valueChecker;
@@ -71,10 +87,9 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
* @return 可缺失的值
* @throws IllegalArgumentException key 不存在时抛出。
*/
@Nonnull
public Optional<V> get(K key) {
if (!this.map.containsKey(key)) {
throw new IllegalArgumentException("Key does not exist");
}
AssertTools.checkArgument(this.map.containsKey(key), "Key does not exist");
return Optional.ofNullable(this.map.get(key));
}
@@ -180,8 +195,7 @@ public abstract class AbstractMapWrapper<K, V, T extends AbstractMapWrapper<K, V
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("rawtypes")
AbstractMapWrapper other = (AbstractMapWrapper) obj;
AbstractMapWrapper<?, ?, ?> other = (AbstractMapWrapper<?, ?, ?>) obj;
return Objects.equals(map, other.map);
}

View File

@@ -21,8 +21,16 @@ import java.util.Map;
import javax.annotation.Nullable;
/**
* 集合工具类
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 0.1.0
*/
public class CollectionTools {
// TODO [添加] 新增其它集合类型,如 guava 的扩展集合等
// isEmpty
public static boolean isEmpty(@Nullable Collection<?> collection) {

View File

@@ -26,6 +26,16 @@ import java.util.function.Consumer;
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
public final class MapWrapper<K, V> extends AbstractMapWrapper<K, V, MapWrapper<K, V>> {

View File

@@ -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();
}
}
}

View File

@@ -115,7 +115,7 @@ public enum Quarter {
// StaticFactoryMethods end
// computs
// computes
public Quarter plus(long quarters) { // TODO 单元测试
final int amount = (int) ((quarters % 4) + 4);
@@ -126,7 +126,7 @@ public enum Quarter {
return plus(-(quarters % 4));
}
// computs end
// computes end
// Getters

View File

@@ -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));
}
}