предисловие
Обычно мы инкапсулируем некоторый кеш обработки или другие небольшие инструменты. Но каждый инкапсулирует его один раз и заново изобретает велосипед, что занимает немного времени. Есть ли хорошая библиотека инструментов, которую можно порекомендовать - guava. Guava — это библиотека с открытым исходным кодом, разработанная Google на основе Java. Ее производительность и практичность лучше, чем у наших собственных колес. В конце концов, она производится Google. Вот несколько часто используемых инструментов guava.
- LoadingCache (локальный кеш)
- Мультикарта и мультисет
- BiMap
- Стол
- Наборы и карты (пересечение и различие)
- EventBus (событие)
- Секундомер
- Файлы (файловые операции)
- RateLimiter
- Гуава Повторить
Следите за официальной учетной записью, общайтесь друг с другом и ищите в WeChat: Sneak forward
введена конфигурация maven гуавы
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
LoadingCache
- LoadingCache широко используется в практических сценариях.Обычно, если вы сталкиваетесь со сценарием, который требует много времени для вычисления или кэширования значений, вам следует сохранить значения в кэше. LoadingCache похож на ConcurrentMap, но не то же самое. Самая большая разница заключается в том, что ConcurrentMap будет постоянно хранить все значения элементов, пока они не будут явно удалены, а LoadingCache автоматически удалит просроченные значения в соответствии с конфигурацией, чтобы разумно использовать память.
- Как правило, кэширование Guava подходит для следующих сценариев:
- Потратьте немного памяти в обмен на скорость
- Некоторые клавиши будут вызываться более одного раза
- Кэшируемый контент ограничен и не будет превышать значение объема памяти. Кэши Guava не будут хранить контент в файлах или вне сервера. Если есть такая необходимость, рассмотрите возможность использования Memcached, Redis
- LoadingCache не может кэшировать нулевые ключи
- CacheBuilder конструирует введение параметра LoadingCache
Параметры метода CacheBuilder |
описывать |
initialCapacity(int initialCapacity) |
Начальный размер буферного пула |
concurrencyLevel(int concurrencyLevel) |
установить параллелизм |
maximumSize(long maximumSize) |
Размер пула кеша, когда элементы кеша приближаются к этому размеру, Гуава начинает перерабатывать старые элементы кеша. |
weakValues() |
Ссылка на хранилище для значения настройки является фантомной ссылкой |
softValues() |
Установите ссылку на хранилище значения как мягкую ссылку |
expireAfterWrite(long duration, TimeUnit unit) |
Если заданный объект времени не записан, объект будет удален из памяти (нерегулярно поддерживается в другом потоке) |
expireAfterAccess(long duration, TimeUnit unit) |
Когда к заданному объекту времени нет доступа для чтения/записи, объект удаляется из памяти (поддерживается нерегулярно в другом потоке) |
refreshAfterWrite(long duration, TimeUnit unit) |
Аналогичен expireAfterWrite, но вместо немедленного удаления ключа он будет обновлен при следующем обновлении, в течение которого может быть возвращено старое значение. |
removalListener( RemovalListener<? super K1, ? super V1> listener) |
Прослушиватель, который срабатывает при удалении кэшированного элемента |
build(CacheLoader<? super K1, V1> loader) |
Если данные не существуют, используйте загрузчик для загрузки данных |
- LoadingCache
V get(K key)
, Получите кешированное значение, если ключ не имеет значения, он вызовет метод загрузки CacheLoader для загрузки нового значения в ключ
- Пример
LoadingCache<Integer,Long> cacheMap = CacheBuilder.newBuilder().initialCapacity(10)
.concurrencyLevel(10)
.expireAfterAccess(Duration.ofSeconds(10))
.weakValues()
.recordStats()
.removalListener(new RemovalListener<Integer,Long>(){
@Override
public void onRemoval(RemovalNotification<Integer, Long> notification) {
System.out.println(notification.getValue());
}
})
.build(new CacheLoader<Integer,Long>(){
@Override
public Long load(Integer key) throws Exception {
return System.currentTimeMillis();
}
});
cacheMap.get(1);
Мультикарта и Мультисет
- Особенность Multimap в том, что он может содержать несколько значений повторяющихся ключей, а также может помещать в несколько разных значений, но один и тот же ключ, но при этом не перезаписывать предыдущее содержимое.
- Пример
//Multimap: key-value key可以重复,value也可重复
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("csc","1");
multimap.put("lwl","1");
multimap.put("csc","1");
multimap.put("lwl","one");
System.out.println(multimap.get("csc"));
System.out.println(multimap.get("lwl"));
---------------------------
[1, 1]
[1, one]
- Относительно полезным сценарием для MultiSet является отслеживание количества объектов каждого типа, поэтому его можно использовать для количественной статистики.
- Пример
//MultiSet: 无序+可重复 count()方法获取单词的次数 增强了可读性+操作简单
Multiset<String> set = HashMultiset.create();
set.add("csc");
set.add("lwl");
set.add("csc");
System.out.println(set.size());
System.out.println(set.count("csc"));
---------------------------
3
2
BiMap
- Ключ BiMap должен быть уникальным, и значение также должно быть уникальным, что может реализовать взаимное преобразование значения и ключа.
- Пример
BiMap<Integer,String> biMap = HashBiMap.create();
biMap.put(1,"lwl");
biMap.put(2,"csc");
BiMap<String, Integer> map = biMap.inverse(); // value和key互转
map.forEach((v, k) -> System.out.println(v + "-" + k));
Table
-
Table<R,C,V> table = HashBasedTable.create();
, как видно из обобщенного типа, таблица совместно определяется двойным первичным ключом R (строка) и C (столбец), а V — хранимое значение
- Добавлены данные:
table.put(R,C,V)
- получить данные:
V v = table.get(R,C)
- Перебрать данные:
Set<R> set = table.rowKeySet(); Set<C> set = table.columnKeySet();
- Пример
// 双键的Map Map--> Table-->rowKey+columnKey+value
Table<String, String, Integer> tables = HashBasedTable.create();
tables.put("csc", "lwl", 1);
//row+column对应的value
System.out.println(tables.get("csc","lwl"));
Наборы и карты
// 不可变集合的创建
ImmutableList<String> iList = ImmutableList.of("csc", "lwl");
ImmutableSet<String> iSet = ImmutableSet.of("csc", "lwl");
ImmutableMap<String, String> iMap = ImmutableMap.of("csc", "hello", "lwl", "world");
пересечение, объединение, разность множеств
HashSet setA = newHashSet(1, 2, 3, 4, 5);
HashSet setB = newHashSet(4, 5, 6, 7, 8);
//并集
SetView union = Sets.union(setA, setB);
//差集 setA-setB
SetView difference = Sets.difference(setA, setB);
//交集
SetView intersection = Sets.intersection(setA, setB);
Пересечение, Союз, Разность карт
HashMap<String, Integer> mapA = Maps.newHashMap();
mapA.put("a", 1);mapA.put("b", 2);mapA.put("c", 3);
HashMap<String, Integer> mapB = Maps.newHashMap();
mapB.put("b", 20);mapB.put("c", 3);mapB.put("d", 4);
MapDifference<String, Integer> mapDifference = Maps.difference(mapA, mapB);
//mapA 和 mapB 相同的 entry
System.out.println(mapDifference.entriesInCommon());
//mapA 和 mapB key相同的value不同的 entry
System.out.println(mapDifference.entriesDiffering());
//只存在 mapA 的 entry
System.out.println(mapDifference.entriesOnlyOnLeft());
//只存在 mapB 的 entry
System.out.println(mapDifference.entriesOnlyOnRight());;
-------------结果-------------
{c=3}
{b=(2, 20)}
{a=1}
{d=4}
EventBus
- EventBus — это механизм обработки событий Guava и элегантная реализация шаблона Observer (модель программирования производитель/потребитель) в шаблонах проектирования. Для прослушивания событий и режима публикации-подписки
- Внутренний принцип реализации EventBus не сложен. EventBus будет поддерживать карту Multimap, Subscriber>. Ключ представляет класс, соответствующий сообщению (разные сообщения имеют разные классы, разные сообщения различаются), а значение является подписчиком, который на самом деле является подписчиком. Соответствует обработчику сообщений. Если есть сообщение для публикации, перейдите на эту карту, чтобы найти подписчика, соответствующего сообщению для выполнения.
- Пример использования
@Data
@AllArgsConstructor
public class OrderMessage {
String message;
}
//使用 @Subscribe 注解,表明使用dealWithEvent 方法处理 OrderMessage类型对应的消息
//可以注解多个方法,不同的方法 处理不同的对象消息
public class OrderEventListener {
@Subscribe
public void dealWithEvent(OrderMessage event) {
System.out.println("内容:" + event.getMessage());
}
}
-------------------------------------
// new AsyncEventBus(String identifier, Executor executor);
EventBus eventBus = new EventBus("lwl");
eventBus.register(new OrderEventListener());
// 发布消息
eventBus.post(new OrderMessage("csc"));
StopWatch
Stopwatch stopwatch = Stopwatch.createStarted();
for(int i=0; i<100000; i++){
// do some thing
}
long nanos = stopwatch.elapsed(TimeUnit.MILLISECONDS);
System.out.println("逻辑代码运行耗时:"+nanos);
Файлы файловые операции
File newFile = new File("D:/text.txt");
Files.write("this is a test".getBytes(), newFile);
//再次写入会把之前的内容冲掉
Files.write("csc".getBytes(), newFile);
//追加写
Files.append("lwl", newFile, Charset.defaultCharset());
File newFile = new File("E:/text.txt");
List<String> lines = Files.readLines(newFile, Charset.defaultCharset());
метод |
описывать |
Files.copy(File from, File to) |
копировать файл |
Files.deleteDirectoryContents(File directory) |
Удалить содержимое папки (включая файлы и подпапки) |
Files.deleteRecursively(File file) |
удалить файл или папку |
Files.move(File from, File to) |
переместить файлы |
Files.touch(File file) |
Отметка времени создания или обновления файла |
Files.getFileExtension(String file) |
получить расширение файла |
Files.getNameWithoutExtension(String file) |
получить имя файла без расширения |
Files.map(File file, MapMode mode) |
Получить отображенный в память буфер |
RateLimiter
//RateLimiter 构造方法,每秒限流permitsPerSecond
public static RateLimiter create(double permitsPerSecond)
//每秒限流 permitsPerSecond,warmupPeriod 则是数据初始预热时间,从第一次acquire 或 tryAcquire 执行开时计算
public static RateLimiter create(double permitsPerSecond, Duration warmupPeriod)
//获取一个令牌,阻塞,返回阻塞时间
public double acquire()
//获取 permits 个令牌,阻塞,返回阻塞时间
public double acquire(int permits)
//获取一个令牌,超时返回
public boolean tryAcquire(Duration timeout)
////获取 permits 个令牌,超时返回
public boolean tryAcquire(int permits, Duration timeout)
RateLimiter limiter = RateLimiter.create(2, 3, TimeUnit.SECONDS);
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
System.out.println("get one permit cost time: " + limiter.acquire(1) + "s");
--------------- 结果 -------------------------
get one permit cost time: 0.0s
get one permit cost time: 1.331672s
get one permit cost time: 0.998392s
get one permit cost time: 0.666014s
get one permit cost time: 0.498514s
get one permit cost time: 0.498918s
get one permit cost time: 0.499151s
get one permit cost time: 0.488548s
- Поскольку RateLimiter обрабатывается с задержкой, первое время, независимо от того, сколько времени было принято, составляет ноль секунд.
- Вы можете видеть, что в первых четырех сборах данных потребовалось три секунды, чтобы прогреть данные, а время между пятым и восьмым захватами имеет тенденцию быть гладким.
Guava Retry
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
- Конструктор RetryerBuilder
Метод RetryerBuilder |
описывать |
withRetryListener |
повторите прослушиватель |
withWaitStrategy |
Интервал повтора после сбоя |
withStopStrategy |
стоп стратегия |
withBlockStrategy |
Стратегия блокировки BlockStrategy |
withAttemptTimeLimiter |
Выполнить политику ограничения времени |
retryIfException |
Если возникает исключение, попробуйте еще раз |
retryIfRuntimeException |
Если возникает исключение RuntimeException, повторите попытку. |
retryIfExceptionOfType(Class<? extends Throwable> ex) |
Если возникает исключение ex, попробуйте еще раз |
retryIfException(Predicate<Throwable> exceptionPredicate) |
Следует ли повторить ненормальное суждение |
retryIfResult(Predicate<V> resultPredicate) |
Судя по возвращаемому результату, следует ли повторить попытку |
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
.retryIfException()
.retryIfResult(Predicates.equalTo(false))
.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(1, TimeUnit.SECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(5))
.build();
//Retryer调用
retryer.call(() -> true);
Приветствуется указание на ошибки в тексте (рассказ чисто вымышленный, любое сходство чисто случайное)
Справочная статья