Merge branch 'v5-master' into feature/filter-readline

# Conflicts:
#	hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
This commit is contained in:
likeguo 2023-03-22 23:39:02 +08:00
commit 8da20d7638
1190 changed files with 52246 additions and 11556 deletions

View File

@ -7,4 +7,13 @@
### 修改描述(包括说明bug修复或者添加新特性)
1. [bug修复] balabala……
2. [新特性] balabala……
2. [新特性] balabala……
### 提交前自测
> 请在提交前自测确保代码没有问题,提交新代码应包含:测试用例、通过(mvn javadoc:javadoc)检验详细注释。
1. 本地如有多个JDK版本可以设置临时JDk版本,如:`export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home`具体替换为本地jdk目录
2. 确保本地测试使用JDK8最新版本`echo $JAVA_HOME`、`mvn -v`、`java -version`均正确。
3. 执行打包生成文档,使用`mvn clean package -Dmaven.test.skip=true -U`,并确认通过,会自动执行打包、生成文档
4. 如需要单独执行文档生成,执行:`mvn javadoc:javadoc `,并确认通过
5. 如需要单独执行测试用例,执行:`mvn clean test`,并确认通过

View File

@ -7,4 +7,13 @@
### 修改描述(包括说明bug修复或者添加新特性)
1. [bug修复] balabala……
2. [新特性] balabala……
2. [新特性] balabala……
### 提交前自测
> 请在提交前自测确保代码没有问题,提交新代码应包含:测试用例、通过(mvn javadoc:javadoc)检验详细注释。
1. 本地如有多个JDK版本可以设置临时JDk版本,如:`export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home`具体替换为本地jdk目录
2. 确保本地测试使用JDK8最新版本`echo $JAVA_HOME`、`mvn -v`、`java -version`均正确。
3. 执行打包生成文档,使用`mvn clean package -Dmaven.test.skip=true -U`,并确认通过,会自动执行打包、生成文档
4. 如需要单独执行文档生成,执行:`mvn javadoc:javadoc `,并确认通过
5. 如需要单独执行测试用例,执行:`mvn clean test`,并确认通过

2050
CHANGELOG.md Normal file → Executable file

File diff suppressed because it is too large Load Diff

1878
CHANGELOG_5.0-5.7.md Executable file

File diff suppressed because it is too large Load Diff

22
README-EN.md Normal file → Executable file
View File

@ -12,7 +12,7 @@
<a target="_blank" href="https://search.maven.org/artifact/cn.hutool/hutool-all">
<img src="https://img.shields.io/maven-central/v/cn.hutool/hutool-all.svg?label=Maven%20Central" />
</a>
<a target="_blank" href="https://license.coscl.org.cn/MulanPSL2/">
<a target="_blank" href="http://license.coscl.org.cn/MulanPSL2/index.html">
<img src="https://img.shields.io/:license-MulanPSL2-blue.svg" />
</a>
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html">
@ -40,8 +40,8 @@
<br/>
<p align="center">
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=jPq9DsjXs7GUWbXRZU3wygSJyMEy4pqr&jump_from=webapi">
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A5-610134978-orange"/></a>
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=QtsqXLkHpLjE99tkre19j6pjPMhSay1a&jump_from=webapi">
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A6-715292493-orange"/></a>
</p>
-------------------------------------------------------------------------------
@ -51,7 +51,7 @@
-------------------------------------------------------------------------------
## 📚Introduction
**Hutool** is a small but comprehensive library of Java tools, encapsulation by static methods, reduce the cost of learning related APIs, increase productivity, and make Java as elegant as a functional programming language,let the Java be "sweet" too.
**Hutool** is a small but comprehensive library of Java tools, achieved by encapsulation through static methods, reduce the cost of learning related APIs, increase productivity, and make Java as elegant as a functional programming language,let the Java be "sweet" too.
**Hutool** tools and methods from each user's crafted, it covers all aspects of the underlying code of Java development, it is a powerful tool for large project development to solve small problems, but also the efficiency of small projects;
@ -111,6 +111,8 @@ Each module can be introduced individually, or all modules can be introduced by
[📘Chinese documentation](https://www.hutool.cn/docs/)
[📘Chinese back-up documentation](https://plus.hutool.cn/docs/#/)
[📙API](https://apidoc.gitee.com/dromara/hutool/)
[🎬Video](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
@ -131,7 +133,7 @@ If you think Hutool is good, you can donate to buy the author a pack of chili~,
We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop
👉 [Hutool Shop](https://m.tb.cn/h.fVxoBOm?sm=2756b2) 👈
👉 [Hutool Shop](https://market.m.taobao.com/apps/market/content/index.html?wh_weex=true&contentId=331724720170) 👈
-------------------------------------------------------------------------------
@ -142,18 +144,18 @@ We provide the T-Shirt and Sweater with Hutool Logo, please visit the shop
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</dependency>
```
### 🍐Gradle
```
implementation 'cn.hutool:hutool-all:5.7.15'
implementation 'cn.hutool:hutool-all:5.8.15'
```
## 📥Download
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.15/)
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.15/)
> 🔔note:
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
@ -205,8 +207,6 @@ Hutool welcomes anyone to contribute code to Hutool, but the author suffers from
## ⭐Star Hutool
[![Giteye chart](https://chart.giteye.net/gitee/dromara/hutool/GMSL7VDA.png)](https://giteye.net/chart/GMSL7VDA)
[![Stargazers over time](https://starchart.cc/dromara/hutool.svg)](https://starchart.cc/dromara/hutool)
## 📌WeChat Official Account
@ -214,4 +214,4 @@ Hutool welcomes anyone to contribute code to Hutool, but the author suffers from
<div align="center">
<img src="https://cdn.jsdelivr.net/gh/looly/hutool-site/images/qr_tuling.jpg" height="150">
<img src="https://dromara.org/img/qrcode/qrcode_1.png" height="150">
</div>
</div>

24
README.md Normal file → Executable file
View File

@ -12,7 +12,7 @@
<a target="_blank" href="https://search.maven.org/artifact/cn.hutool/hutool-all">
<img src="https://img.shields.io/maven-central/v/cn.hutool/hutool-all.svg?label=Maven%20Central" />
</a>
<a target="_blank" href="https://license.coscl.org.cn/MulanPSL2/">
<a target="_blank" href="http://license.coscl.org.cn/MulanPSL2/index.html">
<img src="https://img.shields.io/:license-MulanPSL2-blue.svg" />
</a>
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html">
@ -40,8 +40,8 @@
<br/>
<p align="center">
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=jPq9DsjXs7GUWbXRZU3wygSJyMEy4pqr&jump_from=webapi">
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A5-610134978-orange"/></a>
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=QtsqXLkHpLjE99tkre19j6pjPMhSay1a&jump_from=webapi">
<img src="https://img.shields.io/badge/QQ%E7%BE%A4%E2%91%A6-715292493-orange"/></a>
</p>
-------------------------------------------------------------------------------
@ -107,6 +107,8 @@ Hutool的存在就是为了减少代码搜索成本避免网络上参差不
[📘中文文档](https://www.hutool.cn/docs/)
[📘中文备用文档](https://plus.hutool.cn/docs/#/)
[📙参考API](https://apidoc.gitee.com/dromara/hutool/)
[🎬视频介绍](https://www.bilibili.com/video/BV1bQ4y1M7d9?p=2)
@ -129,7 +131,7 @@ Hutool的存在就是为了减少代码搜索成本避免网络上参差不
我们提供了印有Hutool Logo的周边商品欢迎点击购买支持
👉 [Hutool 周边商店](https://m.tb.cn/h.fVxoBOm?sm=2756b2) 👈
👉 [Hutool 周边商店](https://market.m.taobao.com/apps/market/content/index.html?wh_weex=true&contentId=331724720170) 👈
-------------------------------------------------------------------------------
@ -142,20 +144,20 @@ Hutool的存在就是为了减少代码搜索成本避免网络上参差不
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</dependency>
```
### 🍐Gradle
```
implementation 'cn.hutool:hutool-all:5.7.15'
implementation 'cn.hutool:hutool-all:5.8.15'
```
### 📥下载jar
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.7.15/)
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.15/)
> 🔔️注意
> Hutool 5.x支持JDK8+对Android平台没有测试不能保证所有工具类或工具方法可用。
@ -209,18 +211,16 @@ Hutool欢迎任何人为Hutool添砖加瓦贡献代码不过维护者是
2. Hutool的缩进按照Eclipse~~不要跟我说IDEA多好用维护者非常懒学不会~~IDEA真香改了Eclipse快捷键后舒服多了默认tab缩进所以请遵守不要和我争执空格与tab的问题这是一个病人的习惯
3. 新加的方法不要使用第三方库的方法Hutool遵循无依赖原则除非在extra模块中加方法工具
4. 请pull request到`v5-dev`分支。Hutool在5.x版本后使用了新的分支`v5-master`是主分支表示已经发布中央库的版本这个分支不允许pr也不允许修改。
5. 我们如果关闭了你的issue或pr请不要诧异这是我们保持问题处理整洁的一种方式你依旧可以继续讨论当有讨论结果时我们会重新打开。
-------------------------------------------------------------------------------
## ⭐Star Hutool
[![Giteye chart](https://chart.giteye.net/gitee/dromara/hutool/GMSL7VDA.png)](https://giteye.net/chart/GMSL7VDA)
[![Stargazers over time](https://starchart.cc/dromara/hutool.svg)](https://starchart.cc/dromara/hutool)
## 📌公众号
## 📌 知识星球
<div align="center">
<img src="https://cdn.jsdelivr.net/gh/looly/hutool-site/images/qr_tuling.jpg" height="150">
<img src="https://dromara.org/img/qrcode/qrcode_1.png" height="150">
<img src="https://hutool.cn/images/dromara/zsxq.jpg" height="150">
</div>

View File

@ -7,5 +7,5 @@ echo ' / /_/ // / / // __// __ \ / __ \ / / '
echo ' / __ // /_/ // /_ / /_/ // /_/ // / '
echo '/_/ /_/ \____/ \__/ \____/ \____//_/ '
echo ''
echo '-----------http://hutool.cn/------------'
echo '-----------https://hutool.cn/-----------'
echo '========================================'

View File

@ -1 +1 @@
5.7.15
5.8.15

2
docs/js/version.js Normal file → Executable file
View File

@ -1 +1 @@
var version = '5.7.15'
var version = '5.8.15'

2
hutool-all/pom.xml Normal file → Executable file
View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</parent>
<artifactId>hutool-all</artifactId>

6
hutool-aop/pom.xml Normal file → Executable file
View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</parent>
<artifactId>hutool-aop</artifactId>
@ -17,9 +17,10 @@
<description>Hutool 动态代理AOP</description>
<properties>
<Automatic-Module-Name>cn.hutool.aop</Automatic-Module-Name>
<!-- versions -->
<cglib.version>3.3.0</cglib.version>
<spring.version>5.3.10</spring.version>
<spring.version>5.3.23</spring.version>
</properties>
<dependencies>
@ -43,4 +44,5 @@
<optional>true</optional>
</dependency>
</dependencies>
</project>

0
hutool-aop/src/main/java/cn/hutool/aop/ProxyUtil.java Normal file → Executable file
View File

View File

View File

View File

View File

View File

@ -54,12 +54,13 @@ public class JdkInterceptor implements InvocationHandler, Serializable {
throw e;
}
}
// 结束执行回调
if (aspect.after(target, method, args, result)) {
return result;
}
}
// 结束执行回调
if (aspect.after(target, method, args, result)) {
return result;
}
return null;
}

View File

View File

View File

View File

View File

View File

View File

7
hutool-bloomFilter/pom.xml Normal file → Executable file
View File

@ -9,13 +9,17 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</parent>
<artifactId>hutool-bloomFilter</artifactId>
<name>${project.artifactId}</name>
<description>Hutool 布隆过滤器</description>
<properties>
<Automatic-Module-Name>cn.hutool.bloomfilter</Automatic-Module-Name>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
@ -23,4 +27,5 @@
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -8,7 +8,7 @@ import cn.hutool.bloomfilter.filter.SDBMFilter;
import cn.hutool.core.util.NumberUtil;
/**
* BlommFilter 实现 <br>
* BloomFilter 实现 <br>
* 1.构建hash算法 <br>
* 2.散列hash映射到数组的bit位置 <br>
* 3.验证<br>
@ -79,4 +79,4 @@ public class BitMapBloomFilter implements BloomFilter {
}
return true;
}
}
}

View File

@ -2,19 +2,21 @@ package cn.hutool.bloomfilter;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HashUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.BitSet;
/**
* BloomFilter实现方式2此方式使用BitSet存储<br>
* Hash算法的使用使用固定顺序只需指定个数即可
* @author loolly
*
* @author loolly
*/
public class BitSetBloomFilter implements BloomFilter{
public class BitSetBloomFilter implements BloomFilter {
private static final long serialVersionUID = 1L;
private final BitSet bitSet;
@ -39,22 +41,36 @@ public class BitSetBloomFilter implements BloomFilter{
/**
* 通过文件初始化过滤器.
*
* @param path 文件路径
* @param path 文件路径
* @param charsetName 字符集
* @throws IOException IO异常
* @deprecated 请使用 {@link #init(String, Charset)}
*/
@Deprecated
public void init(String path, String charsetName) throws IOException {
init(path, CharsetUtil.charset(charsetName));
}
/**
* 通过文件初始化过滤器.
*
* @param path 文件路径
* @param charset 字符集
* @throws IOException IO异常
* @since 5.8.0
*/
public void init(String path, String charset) throws IOException {
public void init(String path, Charset charset) throws IOException {
BufferedReader reader = FileUtil.getReader(path, charset);
try {
String line;
while(true) {
while (true) {
line = reader.readLine();
if(line == null) {
if (line == null) {
break;
}
this.add(line);
}
}finally {
} finally {
IoUtil.close(reader);
}
}
@ -75,6 +91,7 @@ public class BitSetBloomFilter implements BloomFilter{
/**
* 判定是否包含指定字符串
*
* @param str 字符串
* @return 是否包含存在误差
*/
@ -101,13 +118,13 @@ public class BitSetBloomFilter implements BloomFilter{
/**
* 将字符串的字节表示进行多哈希编码.
*
* @param str 待添加进过滤器的字符串字节表示.
* @param str 待添加进过滤器的字符串字节表示.
* @param hashNumber 要经过的哈希个数.
* @return 各个哈希的结果数组.
*/
public static int[] createHashes(String str, int hashNumber) {
int[] result = new int[hashNumber];
for(int i = 0; i < hashNumber; i++) {
for (int i = 0; i < hashNumber; i++) {
result[i] = hash(str, i);
}
@ -116,8 +133,9 @@ public class BitSetBloomFilter implements BloomFilter{
/**
* 计算Hash值
*
* @param str 被计算Hash的字符串
* @param k Hash算法序号
* @param k Hash算法序号
* @return Hash值
*/
public static int hash(String str, int k) {
@ -142,4 +160,4 @@ public class BitSetBloomFilter implements BloomFilter{
return 0;
}
}
}
}

View File

@ -32,22 +32,22 @@ public class IntMap implements BitMap, Serializable {
@Override
public void add(long i) {
int r = (int) (i / BitMap.MACHINE32);
int c = (int) (i % BitMap.MACHINE32);
int c = (int) (i & (BitMap.MACHINE32 - 1));
ints[r] = ints[r] | (1 << c);
}
@Override
public boolean contains(long i) {
int r = (int) (i / BitMap.MACHINE32);
int c = (int) (i % BitMap.MACHINE32);
int c = (int) (i & (BitMap.MACHINE32 - 1));
return ((ints[r] >>> c) & 1) == 1;
}
@Override
public void remove(long i) {
int r = (int) (i / BitMap.MACHINE32);
int c = (int) (i % BitMap.MACHINE32);
int c = (int) (i & (BitMap.MACHINE32 - 1));
ints[r] &= ~(1 << c);
}
}
}

View File

@ -32,22 +32,22 @@ public class LongMap implements BitMap, Serializable {
@Override
public void add(long i) {
int r = (int) (i / BitMap.MACHINE64);
long c = i % BitMap.MACHINE64;
long c = i & (BitMap.MACHINE64 - 1);
longs[r] = longs[r] | (1L << c);
}
@Override
public boolean contains(long i) {
int r = (int) (i / BitMap.MACHINE64);
long c = i % BitMap.MACHINE64;
long c = i & (BitMap.MACHINE64 - 1);
return ((longs[r] >>> c) & 1) == 1;
}
@Override
public void remove(long i) {
int r = (int) (i / BitMap.MACHINE64);
long c = i % BitMap.MACHINE64;
long c = i & (BitMap.MACHINE64 - 1);
longs[r] &= ~(1L << c);
}
}
}

View File

@ -14,9 +14,11 @@ import cn.hutool.bloomfilter.bitMap.LongMap;
public abstract class AbstractFilter implements BloomFilter {
private static final long serialVersionUID = 1L;
protected static int DEFAULT_MACHINE_NUM = BitMap.MACHINE32;
private BitMap bm = null;
protected long size = 0;
protected long size;
/**
* 构造
@ -34,7 +36,7 @@ public abstract class AbstractFilter implements BloomFilter {
* @param maxValue 最大值
*/
public AbstractFilter(long maxValue) {
this(maxValue, BitMap.MACHINE32);
this(maxValue, DEFAULT_MACHINE_NUM);
}
/**
@ -80,4 +82,4 @@ public abstract class AbstractFilter implements BloomFilter {
* @return HashCode
*/
public abstract long hash(String str);
}
}

View File

@ -7,19 +7,14 @@ import cn.hutool.core.util.HashUtil;
*
* @author loolly
*/
public class DefaultFilter extends AbstractFilter {
public class DefaultFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public DefaultFilter(long maxValue, int machineNumber) {
super(maxValue, machineNumber);
}
public DefaultFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
return HashUtil.javaDefaultHash(str) % size;
public DefaultFilter(long maxValue, int machineNumber) {
super(maxValue, machineNumber, HashUtil::javaDefaultHash);
}
}

View File

@ -2,20 +2,14 @@ package cn.hutool.bloomfilter.filter;
import cn.hutool.core.util.HashUtil;
public class ELFFilter extends AbstractFilter {
public class ELFFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public ELFFilter(long maxValue, int machineNumber) {
super(maxValue, machineNumber);
}
public ELFFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
return HashUtil.elfHash(str) % size;
public ELFFilter(long maxValue, int machineNumber) {
super(maxValue, machineNumber, HashUtil::elfHash);
}
}

View File

@ -2,20 +2,14 @@ package cn.hutool.bloomfilter.filter;
import cn.hutool.core.util.HashUtil;
public class FNVFilter extends AbstractFilter {
public class FNVFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public FNVFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public FNVFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
return HashUtil.fnvHash(str);
public FNVFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::fnvHash);
}
}

View File

@ -0,0 +1,42 @@
package cn.hutool.bloomfilter.filter;
import cn.hutool.bloomfilter.BloomFilter;
import java.util.function.Function;
/**
* 基于Hash函数方法的{@link BloomFilter}
*
* @author looly
* @since 5.8.0
*/
public class FuncFilter extends AbstractFilter {
private static final long serialVersionUID = 1L;
private final Function<String, Number> hashFunc;
/**
* 构造
*
* @param maxValue 最大值
* @param hashFunc Hash函数
*/
public FuncFilter(long maxValue, Function<String, Number> hashFunc) {
this(maxValue, DEFAULT_MACHINE_NUM, hashFunc);
}
/**
* @param maxValue 最大值
* @param machineNum 机器位数
* @param hashFunc Hash函数
*/
public FuncFilter(long maxValue, int machineNum, Function<String, Number> hashFunc) {
super(maxValue, machineNum);
this.hashFunc = hashFunc;
}
@Override
public long hash(String str) {
return hashFunc.apply(str).longValue() % size;
}
}

View File

@ -1,31 +1,16 @@
package cn.hutool.bloomfilter.filter;
public class HfFilter extends AbstractFilter {
import cn.hutool.core.util.HashUtil;
public class HfFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public HfFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public HfFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
int length = str.length() ;
long hash = 0;
for (int i = 0; i < length; i++) {
hash += str.charAt(i) * 3 * i;
}
if (hash < 0) {
hash = -hash;
}
return hash % size;
public HfFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::hfHash);
}
}

View File

@ -1,24 +1,15 @@
package cn.hutool.bloomfilter.filter;
public class HfIpFilter extends AbstractFilter {
import cn.hutool.core.util.HashUtil;
public class HfIpFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public HfIpFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public HfIpFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
int length = str.length();
long hash = 0;
for (int i = 0; i < length; i++) {
hash += str.charAt(i % 4) ^ str.charAt(i);
}
return hash % size;
public HfIpFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::hfIpHash);
}
}

View File

@ -1,30 +1,15 @@
package cn.hutool.bloomfilter.filter;
import cn.hutool.core.util.HashUtil;
public class JSFilter extends AbstractFilter {
public class JSFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public JSFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public JSFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
int hash = 1315423911;
for (int i = 0; i < str.length(); i++) {
hash ^= ((hash << 5) + str.charAt(i) + (hash >> 2));
}
if(hash<0) {
hash*=-1 ;
}
return hash % size;
public JSFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::jsHash);
}
}

View File

@ -2,20 +2,14 @@ package cn.hutool.bloomfilter.filter;
import cn.hutool.core.util.HashUtil;
public class PJWFilter extends AbstractFilter {
public class PJWFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public PJWFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public PJWFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
return HashUtil.pjwHash(str) % size;
public PJWFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::pjwHash);
}
}

View File

@ -2,20 +2,14 @@ package cn.hutool.bloomfilter.filter;
import cn.hutool.core.util.HashUtil;
public class RSFilter extends AbstractFilter {
public class RSFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public RSFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public RSFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
return HashUtil.rsHash(str) % size;
public RSFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::rsHash);
}
}

View File

@ -2,20 +2,14 @@ package cn.hutool.bloomfilter.filter;
import cn.hutool.core.util.HashUtil;
public class SDBMFilter extends AbstractFilter {
public class SDBMFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public SDBMFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public SDBMFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
return HashUtil.sdbmHash(str) % size;
public SDBMFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::sdbmHash);
}
}

View File

@ -2,21 +2,14 @@ package cn.hutool.bloomfilter.filter;
import cn.hutool.core.util.HashUtil;
public class TianlFilter extends AbstractFilter {
public class TianlFilter extends FuncFilter {
private static final long serialVersionUID = 1L;
public TianlFilter(long maxValue, int machineNum) {
super(maxValue, machineNum);
}
public TianlFilter(long maxValue) {
super(maxValue);
this(maxValue, DEFAULT_MACHINE_NUM);
}
@Override
public long hash(String str) {
return HashUtil.tianlHash(str) % size;
public TianlFilter(long maxValue, int machineNum) {
super(maxValue, machineNum, HashUtil::tianlHash);
}
}

2
hutool-bom/pom.xml Normal file → Executable file
View File

@ -9,7 +9,7 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</parent>
<artifactId>hutool-bom</artifactId>

6
hutool-cache/pom.xml Normal file → Executable file
View File

@ -9,13 +9,17 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</parent>
<artifactId>hutool-cache</artifactId>
<name>${project.artifactId}</name>
<description>Hutool 缓存</description>
<properties>
<Automatic-Module-Name>cn.hutool.cache</Automatic-Module-Name>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>

0
hutool-cache/src/main/java/cn/hutool/cache/Cache.java vendored Normal file → Executable file
View File

0
hutool-cache/src/main/java/cn/hutool/cache/CacheListener.java vendored Normal file → Executable file
View File

0
hutool-cache/src/main/java/cn/hutool/cache/CacheUtil.java vendored Normal file → Executable file
View File

0
hutool-cache/src/main/java/cn/hutool/cache/GlobalPruneTimer.java vendored Normal file → Executable file
View File

View File

0
hutool-cache/src/main/java/cn/hutool/cache/file/LFUFileCache.java vendored Normal file → Executable file
View File

0
hutool-cache/src/main/java/cn/hutool/cache/file/LRUFileCache.java vendored Normal file → Executable file
View File

0
hutool-cache/src/main/java/cn/hutool/cache/file/package-info.java vendored Normal file → Executable file
View File

36
hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java vendored Normal file → Executable file
View File

@ -3,14 +3,17 @@ package cn.hutool.cache.impl;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheListener;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
/**
* 超时和限制大小的缓存的默认实现<br>
@ -27,12 +30,12 @@ import java.util.concurrent.locks.ReentrantLock;
public abstract class AbstractCache<K, V> implements Cache<K, V> {
private static final long serialVersionUID = 1L;
protected Map<K, CacheObj<K, V>> cacheMap;
protected Map<Mutable<K>, CacheObj<K, V>> cacheMap;
/**
* 写的时候每个key一把锁降低锁的粒度
*/
protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
protected final SafeConcurrentHashMap<K, Lock> keyLockMap = new SafeConcurrentHashMap<>();
/**
* 返回缓存容量{@code 0}表示无大小限制
@ -84,7 +87,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
if (isFull()) {
pruneCache();
}
cacheMap.put(key, co);
cacheMap.put(MutableObj.of(key), co);
}
// ---------------------------------------------------------------- put end
@ -112,7 +115,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
keyLock.lock();
try {
// 双重检查锁防止在竞争锁的过程中已经有其它线程写入
final CacheObj<K, V> co = cacheMap.get(key);
final CacheObj<K, V> co = getWithoutLock(key);
if (null == co || co.isExpired()) {
try {
v = supplier.call();
@ -130,6 +133,16 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
}
return v;
}
/**
* 获取键对应的{@link CacheObj}
* @param key 实际使用时会被包装为{@link MutableObj}
* @return {@link CacheObj}
* @since 5.8.0
*/
protected CacheObj<K, V> getWithoutLock(K key){
return this.cacheMap.get(MutableObj.of(key));
}
// ---------------------------------------------------------------- get end
@Override
@ -212,7 +225,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
* @since 5.5.9
*/
public Set<K> keySet(){
return this.cacheMap.keySet();
return this.cacheMap.keySet().stream().map(Mutable::get).collect(Collectors.toSet());
}
/**
@ -237,11 +250,20 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
* @return 移除的对象无返回null
*/
protected CacheObj<K, V> removeWithoutLock(K key, boolean withMissCount) {
final CacheObj<K, V> co = cacheMap.remove(key);
final CacheObj<K, V> co = cacheMap.remove(MutableObj.of(key));
if (withMissCount) {
// 在丢失计数有效的情况下移除一般为get时的超时操作此处应该丢失数+1
this.missCount.increment();
}
return co;
}
/**
* 获取所有{@link CacheObj}值的{@link Iterator}形式
* @return {@link Iterator}
* @since 5.8.0
*/
protected Iterator<CacheObj<K, V>> cacheObjIter(){
return this.cacheMap.values().iterator();
}
}

62
hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java vendored Normal file → Executable file
View File

@ -1,27 +1,36 @@
package cn.hutool.cache.impl;
import cn.hutool.core.date.DateUtil;
import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
/**
* 缓存对象
* @author Looly
*
* @param <K> Key类型
* @param <V> Value类型
* @author Looly
*/
public class CacheObj<K, V> implements Serializable{
public class CacheObj<K, V> implements Serializable {
private static final long serialVersionUID = 1L;
protected final K key;
protected final V obj;
/** 上次访问时间 */
private volatile long lastAccess;
/** 访问次数 */
/**
* 上次访问时间
*/
protected volatile long lastAccess;
/**
* 访问次数
*/
protected AtomicLong accessCount = new AtomicLong();
/** 对象存活时长0表示永久存活*/
private final long ttl;
/**
* 对象存活时长0表示永久存活
*/
protected final long ttl;
/**
* 构造
@ -39,6 +48,7 @@ public class CacheObj<K, V> implements Serializable{
/**
* 获取键
*
* @return
* @since 4.0.10
*/
@ -48,6 +58,7 @@ public class CacheObj<K, V> implements Serializable{
/**
* 获取值
*
* @return
* @since 4.0.10
*/
@ -55,6 +66,39 @@ public class CacheObj<K, V> implements Serializable{
return this.obj;
}
/**
* 获取对象存活时长即超时总时长0表示无限
*
* @return 对象存活时长
* @since 5.7.17
*/
public long getTtl() {
return this.ttl;
}
/**
* 获取过期时间返回{@code null}表示永不过期
*
* @return 此对象的过期时间返回{@code null}表示永不过期
* @since 5.7.17
*/
public Date getExpiredTime(){
if(this.ttl > 0){
return DateUtil.date(this.lastAccess + this.ttl);
}
return null;
}
/**
* 获取上次访问时间
*
* @return 上次访问时间
* @since 5.7.17
*/
public long getLastAccess() {
return this.lastAccess;
}
@Override
public String toString() {
return "CacheObj [key=" + key + ", obj=" + obj + ", lastAccess=" + lastAccess + ", accessCount=" + accessCount + ", ttl=" + ttl + "]";
@ -66,7 +110,7 @@ public class CacheObj<K, V> implements Serializable{
* @return 是否过期
*/
protected boolean isExpired() {
if(this.ttl > 0) {
if (this.ttl > 0) {
// 此处不考虑时间回拨
return (System.currentTimeMillis() - this.lastAccess) > this.ttl;
}
@ -81,7 +125,7 @@ public class CacheObj<K, V> implements Serializable{
* @since 4.0.10
*/
protected V get(boolean isUpdateLastAccess) {
if(isUpdateLastAccess) {
if (isUpdateLastAccess) {
lastAccess = System.currentTimeMillis();
}
accessCount.getAndIncrement();

View File

View File

4
hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java vendored Normal file → Executable file
View File

@ -50,7 +50,7 @@ public class FIFOCache<K, V> extends StampedCache<K, V> {
CacheObj<K, V> first = null;
// 清理过期对象并找出链表头部元素先入元素
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
final Iterator<CacheObj<K, V>> values = cacheObjIter();
if (isPruneExpiredActive()) {
// 清理过期对象并找出链表头部元素先入元素
while (values.hasNext()) {
@ -71,7 +71,7 @@ public class FIFOCache<K, V> extends StampedCache<K, V> {
// 清理结束后依旧是满的则删除第一个被缓存的对象
if (isFull() && null != first) {
cacheMap.remove(first.key);
removeWithoutLock(first.key, false);
onRemove(first.key, first.obj);
count++;
}

4
hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java vendored Normal file → Executable file
View File

@ -57,7 +57,7 @@ public class LFUCache<K, V> extends StampedCache<K, V> {
CacheObj<K, V> comin = null;
// 清理过期对象并找出访问最少的对象
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
Iterator<CacheObj<K, V>> values = cacheObjIter();
CacheObj<K, V> co;
while (values.hasNext()) {
co = values.next();
@ -78,7 +78,7 @@ public class LFUCache<K, V> extends StampedCache<K, V> {
if (isFull() && comin != null) {
long minAccessCount = comin.accessCount.get();
values = cacheMap.values().iterator();
values = cacheObjIter();
CacheObj<K, V> co1;
while (values.hasNext()) {
co1 = values.next();

11
hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java vendored Normal file → Executable file
View File

@ -1,5 +1,6 @@
package cn.hutool.cache.impl;
import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.map.FixedLinkedHashMap;
import java.util.Iterator;
@ -42,7 +43,13 @@ public class LRUCache<K, V> extends ReentrantCache<K, V> {
this.timeout = timeout;
//链表key按照访问顺序排序调用get方法后会将这次访问的元素移至头部
cacheMap = new FixedLinkedHashMap<>(capacity);
final FixedLinkedHashMap<Mutable<K>, CacheObj<K, V>> fixedLinkedHashMap = new FixedLinkedHashMap<>(capacity);
fixedLinkedHashMap.setRemoveListener(entry -> {
if(null != listener){
listener.onRemove(entry.getKey().get(), entry.getValue().getValue());
}
});
cacheMap = fixedLinkedHashMap;
}
// ---------------------------------------------------------------- prune
@ -56,7 +63,7 @@ public class LRUCache<K, V> extends ReentrantCache<K, V> {
return 0;
}
int count = 0;
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
Iterator<CacheObj<K, V>> values = cacheObjIter();
CacheObj<K, V> co;
while (values.hasNext()) {
co = values.next();

0
hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java vendored Normal file → Executable file
View File

18
hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java vendored Normal file → Executable file
View File

@ -18,7 +18,7 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
private static final long serialVersionUID = 1L;
// 一些特殊缓存例如使用了LinkedHashMap的缓存由于get方法也会改变Map的结构导致无法使用读写锁
// 最优的解决方案是使用Guava的ConcurrentLinkedHashMap此处使用简化的互斥锁
// TODO 最优的解决方案是使用Guava的ConcurrentLinkedHashMap此处使用简化的互斥锁
protected final ReentrantLock lock = new ReentrantLock();
@Override
@ -36,7 +36,7 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
lock.lock();
try {
// 不存在或已移除
final CacheObj<K, V> co = cacheMap.get(key);
final CacheObj<K, V> co = getWithoutLock(key);
if (co == null) {
return false;
}
@ -59,7 +59,7 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
CacheObj<K, V> co;
lock.lock();
try {
co = cacheMap.get(key);
co = getWithoutLock(key);
} finally {
lock.unlock();
}
@ -83,7 +83,7 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
CopiedIter<CacheObj<K, V>> copiedIterator;
lock.lock();
try {
copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
copiedIterator = CopiedIter.copyOf(cacheObjIter());
} finally {
lock.unlock();
}
@ -115,6 +115,16 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
}
}
@Override
public String toString() {
lock.lock();
try {
return super.toString();
} finally {
lock.unlock();
}
}
/**
* 移除key对应的对象
*

8
hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java vendored Normal file → Executable file
View File

@ -36,7 +36,7 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
final long stamp = lock.readLock();
try {
// 不存在或已移除
final CacheObj<K, V> co = cacheMap.get(key);
final CacheObj<K, V> co = getWithoutLock(key);
if (co == null) {
return false;
}
@ -58,12 +58,12 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
public V get(K key, boolean isUpdateLastAccess) {
// 尝试读取缓存使用乐观读锁
long stamp = lock.tryOptimisticRead();
CacheObj<K, V> co = cacheMap.get(key);
CacheObj<K, V> co = getWithoutLock(key);
if(false == lock.validate(stamp)){
// 有写线程修改了此对象悲观读
stamp = lock.readLock();
try {
co = cacheMap.get(key);
co = getWithoutLock(key);
} finally {
lock.unlockRead(stamp);
}
@ -88,7 +88,7 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
CopiedIter<CacheObj<K, V>> copiedIterator;
final long stamp = lock.readLock();
try {
copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
copiedIterator = CopiedIter.copyOf(cacheObjIter());
} finally {
lock.unlockRead(stamp);
}

5
hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java vendored Normal file → Executable file
View File

@ -1,6 +1,7 @@
package cn.hutool.cache.impl;
import cn.hutool.cache.GlobalPruneTimer;
import cn.hutool.core.lang.mutable.Mutable;
import java.util.HashMap;
import java.util.Iterator;
@ -37,7 +38,7 @@ public class TimedCache<K, V> extends StampedCache<K, V> {
* @param timeout 过期时长
* @param map 存储缓存对象的map
*/
public TimedCache(long timeout, Map<K, CacheObj<K, V>> map) {
public TimedCache(long timeout, Map<Mutable<K>, CacheObj<K, V>> map) {
this.capacity = 0;
this.timeout = timeout;
this.cacheMap = map;
@ -52,7 +53,7 @@ public class TimedCache<K, V> extends StampedCache<K, V> {
@Override
protected int pruneCache() {
int count = 0;
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
final Iterator<CacheObj<K, V>> values = cacheObjIter();
CacheObj<K, V> co;
while (values.hasNext()) {
co = values.next();

23
hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java vendored Normal file → Executable file
View File

@ -1,6 +1,11 @@
package cn.hutool.cache.impl;
import java.util.WeakHashMap;
import cn.hutool.cache.CacheListener;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.map.WeakConcurrentMap;
import java.lang.ref.Reference;
/**
* 弱引用缓存<br>
@ -17,8 +22,22 @@ import java.util.WeakHashMap;
public class WeakCache<K, V> extends TimedCache<K, V>{
private static final long serialVersionUID = 1L;
/**
* 构造
* @param timeout 超时时常单位毫秒-1或0表示无限制
*/
public WeakCache(long timeout) {
super(timeout, new WeakHashMap<>());
super(timeout, new WeakConcurrentMap<>());
}
@Override
public WeakCache<K, V> setListener(CacheListener<K, V> listener) {
super.setListener(listener);
final WeakConcurrentMap<Mutable<K>, CacheObj<K, V>> map = (WeakConcurrentMap<Mutable<K>, CacheObj<K, V>>) this.cacheMap;
// WeakKey回收之后key对应的值已经是null了因此此处的key也为null
map.setPurgeListener((key, value)-> listener.onRemove(Opt.ofNullable(key).map(Reference::get).map(Mutable::get).get(), value.getValue()));
return this;
}
}

0
hutool-cache/src/main/java/cn/hutool/cache/impl/package-info.java vendored Normal file → Executable file
View File

0
hutool-cache/src/main/java/cn/hutool/cache/package-info.java vendored Normal file → Executable file
View File

0
hutool-cache/src/test/java/cn/hutool/cache/CacheConcurrentTest.java vendored Normal file → Executable file
View File

7
hutool-cache/src/test/java/cn/hutool/cache/CacheTest.java vendored Normal file → Executable file
View File

@ -61,6 +61,13 @@ public class CacheTest {
Assert.assertNull(value3);
}
@Test
public void lfuCacheTest2(){
Cache<String, String> lfuCache = CacheUtil.newLFUCache(3);
final String s = lfuCache.get(null);
Assert.assertNull(s);
}
@Test
public void lruCacheTest(){
Cache<String, String> lruCache = CacheUtil.newLRUCache(3);

0
hutool-cache/src/test/java/cn/hutool/cache/FileCacheTest.java vendored Normal file → Executable file
View File

46
hutool-cache/src/test/java/cn/hutool/cache/LRUCacheTest.java vendored Normal file → Executable file
View File

@ -1,10 +1,15 @@
package cn.hutool.cache;
import cn.hutool.cache.impl.LRUCache;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
* https://github.com/dromara/hutool/issues/1895<br>
@ -13,17 +18,29 @@ import java.util.concurrent.CountDownLatch;
*/
public class LRUCacheTest {
@Test
@Ignore
public void putTest(){
//https://github.com/dromara/hutool/issues/2227
final LRUCache<String, String> cache = CacheUtil.newLRUCache(100, 10);
for (int i = 0; i < 10000; i++) {
//ThreadUtil.execute(()-> cache.put(RandomUtil.randomString(5), "1243", 10));
ThreadUtil.execute(()-> cache.get(RandomUtil.randomString(5), ()->RandomUtil.randomString(10)));
}
ThreadUtil.sleep(3000);
}
@Test
public void readWriteTest() throws InterruptedException {
LRUCache<Integer, Integer> cache = CacheUtil.newLRUCache(10);
final LRUCache<Integer, Integer> cache = CacheUtil.newLRUCache(10);
for (int i = 0; i < 10; i++) {
cache.put(i, i);
}
CountDownLatch countDownLatch = new CountDownLatch(10);
final CountDownLatch countDownLatch = new CountDownLatch(10);
// 10个线程分别读0-9 10000次
for (int i = 0; i < 10; i++) {
int finalI = i;
final int finalI = i;
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
cache.get(finalI);
@ -34,7 +51,7 @@ public class LRUCacheTest {
// 等待读线程结束
countDownLatch.await();
// 按顺序读0-9
StringBuilder sb1 = new StringBuilder();
final StringBuilder sb1 = new StringBuilder();
for (int i = 0; i < 10; i++) {
sb1.append(cache.get(i));
}
@ -43,10 +60,29 @@ public class LRUCacheTest {
// 新加11此时0最久未使用应该淘汰0
cache.put(11, 11);
StringBuilder sb2 = new StringBuilder();
final StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < 10; i++) {
sb2.append(cache.get(i));
}
Assert.assertEquals("null123456789", sb2.toString());
}
@Test
public void issue2647Test(){
final AtomicInteger removeCount = new AtomicInteger();
final LRUCache<String, Integer> cache = CacheUtil.newLRUCache(3,1);
cache.setListener((key, value) -> {
// 共移除7次
removeCount.incrementAndGet();
//Console.log("Start remove k-v, key:{}, value:{}", key, value);
});
for (int i = 0; i < 10; i++) {
cache.put(StrUtil.format("key-{}", i), i);
}
Assert.assertEquals(7, removeCount.get());
Assert.assertEquals(3, cache.size());
}
}

View File

@ -0,0 +1,51 @@
package cn.hutool.cache;
import cn.hutool.cache.impl.WeakCache;
import cn.hutool.core.lang.Console;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
public class WeakCacheTest {
@Test
public void removeTest(){
final WeakCache<String, String> cache = new WeakCache<>(-1);
cache.put("abc", "123");
cache.put("def", "456");
Assert.assertEquals(2, cache.size());
// 检查被MutableObj包装的key能否正常移除
cache.remove("abc");
Assert.assertEquals(1, cache.size());
}
@Test
@Ignore
public void removeByGcTest(){
// https://gitee.com/dromara/hutool/issues/I51O7M
WeakCache<String, String> cache = new WeakCache<>(-1);
cache.put("a", "1");
cache.put("b", "2");
// 监听
Assert.assertEquals(2, cache.size());
cache.setListener(Console::log);
// GC测试
int i=0;
while(true){
if(2 == cache.size()){
i++;
Console.log("Object is alive for {} loops - ", i);
System.gc();
}else{
Console.log("Object has been collected.");
Console.log(cache.size());
break;
}
}
}
}

7
hutool-captcha/pom.xml Normal file → Executable file
View File

@ -9,13 +9,17 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</parent>
<artifactId>hutool-captcha</artifactId>
<name>${project.artifactId}</name>
<description>Hutool 验证码工具</description>
<properties>
<Automatic-Module-Name>cn.hutool.captcha</Automatic-Module-Name>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
@ -23,4 +27,5 @@
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

114
hutool-core/README.md Executable file
View File

@ -0,0 +1,114 @@
<p align="center">
<a href="https://hutool.cn/"><img src="https://cdn.jsdelivr.net/gh/looly/hutool-site/images/logo.jpg" width="45%"></a>
</p>
<p align="center">
<strong>🍬A set of tools that keep Java sweet.</strong>
</p>
<p align="center">
👉 <a href="https://hutool.cn">https://hutool.cn/</a> 👈
</p>
## 📚Hutool-core 模块介绍
`Hutool-core`提供了最常使用的基础工具类包括集合、Map、IO、线程、Bean、图片处理、线程并发等便捷工具。
-------------------------------------------------------------------------------
## 🛠️包含内容
### 注解(annotation)
提供了注解工具类,以及一些注解封装。如`CombinationAnnotationElement`组合注解以及Alias别名注解等。
### bean(bean)
提供了Bean工具类以及Bean属性解析、Bean拷贝、动态Bean等。
### 构建器(builder)
抽象了Builder接口提供建造者模式的封装并默认提供了包括equals封装、Bean构建封装、比较器封装等。
### 克隆(clone)
提供`Cloneable`接口,明确`clone`方法,并提供默认实现类。
### 编码(codec)
提供了BaseN编码Base16、Base32、Base58、Base62、Base64编码实现。并提供了包括BCD、PunyCode、百分号编码的实现。
同时提供了包括莫尔斯电码、凯撒密码、RotN这类有趣功能的实现。
### 集合(collection)
集合中主要是提供了针对`Iterator`实现类的工具封装方法`IterUtil`和集合类封装的工具类`CollUtil`,并提供了一些特别的集合封装。
### 比较器(comparator)
主要是一些比较器的实现如Bean字段比较器、自定义函数比较器、版本比较器等。
### 动态编译(compiler)
提供`javax.tools.JavaCompiler`的包装简化服务,形成源码动态编译工具类`CompilerUtil`,完成代码动态编译及热部署。
### 压缩(compress)
主要针对`java.util.zip`中的相关类封装工具提供Zip、Gzip、Zlib等格式的压缩解压缩封装为`ZipUtil`提供服务。
### 转换(convert)
“万能”转换器,提供整套的类型转换方式。通过`Converter`接口和`ConverterRegistry`转换登记中心,完成任意数据类型转换和自定义转换。
### 日期时间(date)
提供`Date`、`Calendar`、`java.time`相关API的工具化封装。包括时间解析、格式化、偏移等。
### 异常(exceptions)
提供异常工具`ExceptionUtil`,以及一些工具内部使用的异常。
### getter接口(getter)
提供各种类型的get操作接口封装。
### 图片(img)
提供图片、绘图、字体等工具封装并提供GIF生成器和解析器实现。
### IO流和文件(io)
提供IO流工具、文件工具、文件类型工具等并提供流拷贝、Checksum、文件监听功能实现。
### 语言特性(lang)
超级大杂项,提供一些设计模式的抽象实现(如单例模式`Singleton`还有正则、Id生成器、函数、Hash算法、可变对象、树形结构、字典等。
### Map(map)
提供Map工具类和各类Map实现封装如行列键的Table实现、自定义键值对转换的Map、线程安全的WeakMap实现等。
### 数学(math)
提供简单数学计算封装,如排列组合、货币类等。
### 网络(net)
提供网络相关工具封装以及Ip地址工具类、SSL工具类、URL编码解码等。
### StreamAPI封装(stream)
提供简单的Stream相关封装。
### Swing和AWT(swing)
提供桌面应用API的工具封装如启动应用、控制键盘鼠标操作、截屏等功能。
### 文本字符串(text)
提供强大的字符串文本封装包括字符转换、字符串查找、字符串替换、字符串切分、Unicode工具等并提供CSV格式封装。
### 线程及并发(thread)
线程并发封装,包括线程工具、锁工具、`CompletableFuture`封装工具、线程池构建等。
### 工具杂项(util)
提供其他不便归类的杂项工具类。如数组、编码、字符、Class、坐标系、身份证、组织机构代码、脱敏、枚举、转义、XML、进制转换、随机数、反射、正则、SPI等各种工具。

5
hutool-core/pom.xml Normal file → Executable file
View File

@ -9,11 +9,14 @@
<parent>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.7.15</version>
<version>5.8.15</version>
</parent>
<artifactId>hutool-core</artifactId>
<name>${project.artifactId}</name>
<description>Hutool核心包括集合、字符串、Bean等工具</description>
<properties>
<Automatic-Module-Name>cn.hutool.core</Automatic-Module-Name>
</properties>
</project>

Some files were not shown because too many files have changed in this diff Show More