mirror of
https://github.com/LamGC/ContentGrabbingJi.git
synced 2025-04-30 06:37:36 +00:00
[Add] 添加 ListCacheStore 的本地实现;
[Add] CopyOnWriteArrayListCacheStore 添加基于 CopyOnWriteArrayList 的 ListCacheStore 实现; [Add] LocalCollectionCacheStore 添加集合型本地缓存存储容器父类, 已实现 CollectionCacheStore 接口; [Add] ListCacheStoreTest 添加对 CopyOnWriteArrayListCacheStore 的完整单元测试(同样包括了 LocalCollectionCacheStore 的测试);
This commit is contained in:
parent
7448379da0
commit
003f571aad
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 LamGC
|
||||||
|
*
|
||||||
|
* ContentGrabbingJi is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* ContentGrabbingJi is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.lamgc.cgj.bot.cache.local;
|
||||||
|
|
||||||
|
import net.lamgc.cgj.bot.cache.ListCacheStore;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于 {@link CopyOnWriteArrayList} 的有序列表缓存存储容器.
|
||||||
|
* @param <E> 元素类型.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
public class CopyOnWriteArrayListCacheStore<E> extends LocalCollectionCacheStore<E, List<E>> implements ListCacheStore<E> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getElement(String key, int index) {
|
||||||
|
List<E> itemCollection = getCacheItemCollection(key, false);
|
||||||
|
try {
|
||||||
|
return itemCollection == null ? null : itemCollection.get(index);
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<E> getElementsByRange(String key, int index, int length) {
|
||||||
|
int listLength = elementsLength(key);
|
||||||
|
if (listLength == -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<E> itemCollection = getCacheItemCollection(key, false);
|
||||||
|
List<E> result = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ListIterator<E> iterator = itemCollection.listIterator(index);
|
||||||
|
for (int i = 0; i < length && iterator.hasNext(); i++) {
|
||||||
|
result.add(iterator.next());
|
||||||
|
}
|
||||||
|
} catch (IndexOutOfBoundsException ignored) {
|
||||||
|
// 正常情况来讲, 该 try-catch 块只有 listIterator 会抛出 IndexOutOfBoundsException,
|
||||||
|
// 而一旦抛出 IndexOutOfBoundsException, 就代表 index 溢出了, try 块后面代码没有继续执行,
|
||||||
|
// 既然抛出异常时, result 并没有添加任何元素, 为何要再 new 一个 List 浪费内存呢? :D
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeElement(String key, int index) {
|
||||||
|
List<E> itemCollection = getCacheItemCollection(key, false);
|
||||||
|
if (itemCollection != null) {
|
||||||
|
try {
|
||||||
|
itemCollection.remove(index);
|
||||||
|
return true;
|
||||||
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<E> getCacheItemCollection(String key, boolean create) {
|
||||||
|
Objects.requireNonNull(key);
|
||||||
|
Map<String, CacheItem<List<E>>> cacheMap = getCacheMap();
|
||||||
|
if (!cacheMap.containsKey(key)) {
|
||||||
|
if (create) {
|
||||||
|
cacheMap.put(key, new CacheItem<>(new CopyOnWriteArrayList<>()));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cacheMap.get(key).getValue();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 LamGC
|
||||||
|
*
|
||||||
|
* ContentGrabbingJi is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* ContentGrabbingJi is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.lamgc.cgj.bot.cache.local;
|
||||||
|
|
||||||
|
import net.lamgc.cgj.bot.cache.CollectionCacheStore;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地集合缓存存储容器.
|
||||||
|
* @param <E> 元素类型
|
||||||
|
* @author LamGC
|
||||||
|
* @see net.lamgc.cgj.bot.cache.CacheStore
|
||||||
|
* @see net.lamgc.cgj.bot.cache.CollectionCacheStore
|
||||||
|
*/
|
||||||
|
public abstract class LocalCollectionCacheStore<E, C extends Collection<E>>
|
||||||
|
extends HashCacheStore<C>
|
||||||
|
implements CollectionCacheStore<E, C> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存项集合对象.
|
||||||
|
* @param key 缓存项键名
|
||||||
|
* @param create 如果不存在, 是否创建.
|
||||||
|
* @return 如果不存在且 create 为 false, 或添加失败, 返回 false, 添加成功返回 true.
|
||||||
|
*/
|
||||||
|
protected abstract C getCacheItemCollection(String key, boolean create);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addElement(String key, E element) {
|
||||||
|
Objects.requireNonNull(key);
|
||||||
|
Objects.requireNonNull(element);
|
||||||
|
Collection<E> itemCollection = getCacheItemCollection(key, true);
|
||||||
|
return itemCollection.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addElements(String key, Collection<E> elements) {
|
||||||
|
Objects.requireNonNull(key);
|
||||||
|
Objects.requireNonNull(elements);
|
||||||
|
Collection<E> itemCollection = getCacheItemCollection(key, true);
|
||||||
|
return itemCollection.addAll(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsElement(String key, E value) {
|
||||||
|
Objects.requireNonNull(key);
|
||||||
|
Objects.requireNonNull(value);
|
||||||
|
Collection<E> itemCollection = getCacheItemCollection(key, false);
|
||||||
|
if (itemCollection == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return itemCollection.contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty(String key) {
|
||||||
|
Collection<E> itemCollection = getCacheItemCollection(Objects.requireNonNull(key), false);
|
||||||
|
if (itemCollection == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return itemCollection.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int elementsLength(String key) {
|
||||||
|
Collection<E> itemCollection = getCacheItemCollection(Objects.requireNonNull(key), false);
|
||||||
|
if (itemCollection == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return itemCollection.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean clearCollection(String key) {
|
||||||
|
Collection<E> itemCollection = getCacheItemCollection(Objects.requireNonNull(key), false);
|
||||||
|
if (itemCollection == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
itemCollection.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeElement(String key, E element) {
|
||||||
|
Objects.requireNonNull(key);
|
||||||
|
Objects.requireNonNull(element);
|
||||||
|
Collection<E> itemCollection = getCacheItemCollection(key, false);
|
||||||
|
if (itemCollection == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return itemCollection.remove(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 LamGC
|
||||||
|
*
|
||||||
|
* ContentGrabbingJi is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* ContentGrabbingJi is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.lamgc.cgj.bot.cache.local;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import net.lamgc.cgj.bot.cache.ListCacheStore;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see CopyOnWriteArrayListCacheStore
|
||||||
|
* @see LocalCollectionCacheStore
|
||||||
|
*/
|
||||||
|
public class ListCacheStoreTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nullThrowTest() {
|
||||||
|
final ListCacheStore<String> cacheStore = new CopyOnWriteArrayListCacheStore<>();
|
||||||
|
|
||||||
|
// LocalCollectionCacheStore
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.addElement(null, "testValue"));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.addElement("testKey", null));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.addElements(null, new ArrayList<>()));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.addElements("testKey", null));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.elementsLength(null));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.containsElement(null, "testValue"));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.containsElement("testKey", null));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.isEmpty(null));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.clearCollection(null));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.removeElement(null, "testValue"));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.removeElement("testKey", null));
|
||||||
|
|
||||||
|
// CopyOnWriteArrayListCacheStore
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.getElement(null, 0));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.getElementsByRange(null, 0, 0));
|
||||||
|
Assert.assertThrows(NullPointerException.class, () -> cacheStore.removeElement(null, 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notExistCacheTest() {
|
||||||
|
final ListCacheStore<String> cacheStore = new CopyOnWriteArrayListCacheStore<>();
|
||||||
|
final String key = "testKey";
|
||||||
|
Assert.assertFalse(cacheStore.clearCollection(key));
|
||||||
|
Assert.assertFalse(cacheStore.isEmpty(key));
|
||||||
|
Assert.assertEquals(-1, cacheStore.elementsLength(key));
|
||||||
|
Assert.assertFalse(cacheStore.containsElement(key, "testValue"));
|
||||||
|
Assert.assertFalse(cacheStore.removeElement(key, "testValue"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addAndGetTest() {
|
||||||
|
final ListCacheStore<Integer> cacheStore = new CopyOnWriteArrayListCacheStore<>();
|
||||||
|
final String key = "test01";
|
||||||
|
List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
|
// getElement/getElementsByRange Cache不存在测试
|
||||||
|
Assert.assertNull(cacheStore.getElement(key, 0));
|
||||||
|
Assert.assertNull(cacheStore.getElementsByRange(key, 0, 1));
|
||||||
|
|
||||||
|
// addElement/getElement 正常情况测试
|
||||||
|
Assert.assertTrue("addElement operation failed!", cacheStore.addElement(key, 0));
|
||||||
|
Assert.assertEquals(new Integer(0), cacheStore.getElement(key, 0));
|
||||||
|
// 超出范围的 null 测试
|
||||||
|
Assert.assertNull(cacheStore.getElement(key, cacheStore.elementsLength(key)));
|
||||||
|
|
||||||
|
// addElements/getElementsByRange 正常情况测试
|
||||||
|
Assert.assertTrue("addElements operation failed!", cacheStore.addElements(key, numbers));
|
||||||
|
Assert.assertEquals(Lists.newArrayList(0, 1, 2), cacheStore.getElementsByRange(key, 0, 3));
|
||||||
|
|
||||||
|
// 不足长度的 getElementsByRange
|
||||||
|
Assert.assertEquals(Lists.newArrayList(7, 8, 9), cacheStore.getElementsByRange(key, 7, 8));
|
||||||
|
|
||||||
|
// 超出索引的 getElementsByRange
|
||||||
|
List<Integer> result = cacheStore.getElementsByRange(key, cacheStore.elementsLength(key) + 1, 8);
|
||||||
|
Assert.assertNotNull("getElementsByRange returned null if index is out of range", result);
|
||||||
|
Assert.assertEquals("getElementsByRange returned a non empty list when the index was out of range",
|
||||||
|
0, result.size());
|
||||||
|
|
||||||
|
// 不足长度的 getElementsByRange
|
||||||
|
Assert.assertEquals(Lists.newArrayList(), cacheStore.getElementsByRange(key, cacheStore.elementsLength(key), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeElementTest() {
|
||||||
|
// removeElement(String, E) / removeElement(String, int)
|
||||||
|
final ListCacheStore<String> cacheStore = new CopyOnWriteArrayListCacheStore<>();
|
||||||
|
final String key = "test01";
|
||||||
|
Random random = new Random();
|
||||||
|
List<String> numbers = Lists.newArrayList("1", "2", "3", "4", "5", "6", "7", "8", "9");
|
||||||
|
|
||||||
|
// 删除不存在 Cache 返回 false
|
||||||
|
Assert.assertFalse(cacheStore.removeElement(key, 0));
|
||||||
|
|
||||||
|
Assert.assertTrue("addElements operation failed!", cacheStore.addElements(key, numbers));
|
||||||
|
|
||||||
|
int removeIndex = random.nextInt(cacheStore.elementsLength(key));
|
||||||
|
numbers.remove(removeIndex);
|
||||||
|
Assert.assertTrue("removeElement operation failed!", cacheStore.removeElement(key, removeIndex));
|
||||||
|
Assert.assertEquals(numbers, cacheStore.getElementsByRange(key, 0, cacheStore.elementsLength(key)));
|
||||||
|
|
||||||
|
String removeTarget = cacheStore.getElement(key, random.nextInt(cacheStore.elementsLength(key)));
|
||||||
|
Assert.assertNotNull(removeTarget);
|
||||||
|
Assert.assertTrue(cacheStore.containsElement(key, removeTarget));
|
||||||
|
numbers.remove(removeTarget);
|
||||||
|
Assert.assertTrue("removeElement operation failed!", cacheStore.removeElement(key, removeTarget));
|
||||||
|
Assert.assertEquals(numbers, cacheStore.getElementsByRange(key, 0, cacheStore.elementsLength(key)));
|
||||||
|
|
||||||
|
Assert.assertTrue("clearCollection operation failed!", cacheStore.clearCollection(key));
|
||||||
|
Assert.assertTrue(cacheStore.exists(key));
|
||||||
|
Assert.assertEquals(0, cacheStore.elementsLength(key));
|
||||||
|
Assert.assertTrue(cacheStore.isEmpty(key));
|
||||||
|
|
||||||
|
// 删除不存在元素返回 false
|
||||||
|
Assert.assertFalse(cacheStore.removeElement(key, cacheStore.elementsLength(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user