常用工具类
String
// 占位符带编号
String url = MessageFormat.format("https://search.bilibili.com/all?keyword={0}", name);
// 占位符不带编号
String url = String.format("https://search.bilibili.com/all?keyword=%s",name);
// 比较文本相似度:https://juejin.cn/post/6844903992812634119
// 业务实体通过反射覆写toString方法 () org.apache.commons.lang
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
Map
https://blog.csdn.net/p812438109/article/details/105758128
TreeMap:Map按key排序
Pattern/Matcher
https://blog.csdn.net/woaigaolaoshi/article/details/50970527
https://vimsky.com/examples/usage/matcher-hitend-method-in-java-with-examples.html
todo
Lists
// 拷贝list
List<TransferOrderBO> updateOrders = Lists.newArrayList(subOrders);
// 切片分组
List<List<T>> partition(List<T> list, int size);
// addAll方法一定要判空,否则会有NPE问题
list.addAll(null)
// 数组转List
//不可变数组
List<String> il = ImmutableList.of("string", "elements"); //推荐,需要用guava
Arrays.asList("element1", "element2");// 有坑:https://blog.csdn.net/youanyyou/article/details/106228442
//可变数组
List<String> l3 = Lists.newArrayList("or", "string", "elements"); //推荐,需要用guava
new ArrayList<>(Arrays.asList(array));
//List转数组
String[] strs = list.toArray(new String[list.size()]);
Integer[] a = list.toArray(new Integer[list.size()]);
int[] b = list.stream().mapToInt(Integer::valueOf).toArray();
//stdout
List<String> list = Arrays.asList("1", "2", "3");
System.out.println("list=" + list); //list=[1, 2, 3] ,已经做了重载
StringUtils
依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
用法
// 判空
StringUtils.isAnyBlank("str1","str2","str3",...)
// A is In (B,C,D...)
StringUtils.equalsAnyIgnoreCase("baseStr","strA","strB",...)
// 分割字符串,直接看源码方法上的注释
// 按字符分割
StringUtils.split(String str, char separatorChar)
// 按字符串分割,("ab de fg", null) = ["ab","de","fg"]
StringUtils.splitByWholeSeparator(String str, String separator)
// 按字符串分割,("ab de fg", null) = ["ab", "", "", "de", "fg"]
StringUtils.splitByWholeSeparatorPreserveAllTokens(String str, String separator)
// 统计字符串中子串出现的次数
StringUtils.countMatches()
// 字符串是否包含 字符/子串
StringUtils.contains()
// 字符串首字母大写以及小写
StringUtils.capitalize()/StringUtils.uncapitalize()
// 转化默认字符串
StringUtils.defaultIfEmpty("str", "defaultStr");
// 拼接字符串,List<String>扁平化
StringUtils.join(list, ";")
StringEscapeUtils
依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.9</version>
</dependency>
用法
// 去除转义
StringEscapeUtils.unescapeJava(url/json)
NumberUtils
// 判断字符串是否是数字
NumberUtils.isNumber("5.96");//结果是true
NumberUtils.isNumber("s5");//结果是false
NumberUtils.isNumber("0000000000596");//结果是true
// 判断字符串中是否全为数字
NumberUtils.isDigits("0000000000.596");//false
NumberUtils.isDigits("0000000000596");//true
FileUtils
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
用法
FileUtils.sizeOf(File file)
Collections
// 空集合,单例
Collections.emptyMap()
Collections.emptyList()
Collections.emptySet()
catch (Exception e) {
...
return Collections.emptyMap(); //单例,不要用Maps.newHashMap(),防止大量错误打满内存
}
// 返回两个集合是否有交集,有交集返回false,否则返回true
boolean Collections.disjoint(Collection<?> c1, Collection<?> c2)
// 乱序
Collections.shuffle(questionBank.choiceQuestionList);
Pair
函数返回值有两个数据时,无需定义结构体
import javafx.util.Pair;
private Pair<Integer, Integer> queryMinAndMaxId() {
//TODO select min(id),max(id) from xxx
return null;
}
Optional
https://www.cnblogs.com/mengw/p/13793712.html
// 让函数返回Optional对象,由调用方去判断
public static <T> Optional<T> op(){
return Optional.empty();
return (Optional<T>)Optional.of(data); //data==null抛空指针异常
return (Optional<T>)Optional.ofNullable(json); //data==null返回Optional.empty()
}
// map和flatMap
// 参数都接收函数,返回对象实例都由Optional包装
// flatMap接收函数接口的返回类型为Optional,map接收其他类型
// flatMap:将多个Optional嵌套缩减成一层Optional嵌套
// https://xiaoyyu.blog.csdn.net/article/details/103251436
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));//直接返回Function执行的结果
}
}
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));//会使用Optional的ofNullable方法包装Function函数返回的值
}
}
Stream
Stream分析:https://blog.csdn.net/justloveyou_/article/details/79562574 最后总结的Stream特性较为经典
Stream分析+源码:https://blog.csdn.net/qq_36263268/article/details/113175067
// map是Intermediate操作,不修改源里的数据
List<String> resultList = new ArrayList<>();
List<String> ano = new ArrayList<>();
resultList.add("a");
resultList.stream().map(str -> ano.add(str));
System.out.println(ano.size()); // output:0
resultList.stream().forEach(str -> ano.add(str));
System.out.println(ano.size()); // output:1
example
Stream操作大全:https://blog.csdn.net/huangjhai/article/details/107852137
// 数组和Stream转化
Stream<T> stream = Arrays.stream(T[]);
T[] array = list.stream().toArray(new T[size]);
stream().anyMatch()
stream().allMatch()
stream().noneMatch()
stream().max()
stream().min()
stream().distinct()
// 判空和数据操作
List<Long> orderIds = Optional.ofNullable(orderResponse)
.map(OrderResponse::getOrderList)
.map(list -> list.stream() //list为空也不会抛异常
.map(Order::getId)
.collect(Collectors.toList()))
.orElse(null);
List<BasicNameValuePair> pairs = Optional.ofNullable(req)
.map(PostFormHttpReq::getParams)
.map(map -> map.entrySet().stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
.collect(Collectors.toList()))
.orElse(Lists.newArrayList());
// 将list<DTO>的DTO中某个元素抽取出来作为新List
List<Long> voucherIds = Optional.ofNullable(orderResp)
.map(OrderResponse::getSubOrders)
.map(
list -> list.stream().map(SubOrderDTO::getVoucherId).collect(Collectors.toList())
).orElse(null);
showList.stream()
.map(ShowBO::getDetails)
.map(list -> list.stream().map(xxx).collect(Collectors.toList()))
.collect(Collectors.toList());
// 聚合为Map
Map<K, V> cacheResult = resultList.stream()
.collect(Collectors.toMap(
item -> buildKey(return K),
item -> buildValue(return V),
(pre, cur) -> pre
));
Stream.foreach
foreach:https://segmentfault.com/a/1190000021006514
foreach和map的区别:https://developer.aliyun.com/article/694263
stream().foreach和Collection.forEach区别:https://baijiahao.baidu.com/s?id=1637952388544934539&wfr=spider&for=pc
生成一个新的对象的时候,使用 map 会更好;只是操作 list 内部的对象时,用 forEach
map方法接收一个功能型接口,功能型接口接收一个参数,返回一个值。map 方法的用途是将旧数据转换后变为新数据,是一种 1:1 的映射,每个输入元素按照规则转换成另一个元素。该方法是 Intermediate 操作
forEach方法并不保证元素消费的先后顺序,forEachOrdered保证元素顺序消费
forEach不能修改自己包含的本地变量值
Stream.flatMap
https://zhaoshuxiang.blog.csdn.net/article/details/106083097
https://www.cnblogs.com/bonelee/p/7814563.html
// List<Map>扁平化为Map
List<Map<String, String>> parallelResult = ...;
return parallelResult.stream().filter(Objects::nonNull)
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));
// List<List<Object>>扁平化为List<Object>
List<List<String>> temp = ...;
List<String> result = temp.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
// 多级平整
Arrays.stream(response.getAllHeaders())
.flatMap(header -> Arrays.stream(header.getElements())
.filter(Objects::nonNull))
.collect(Collectors.toMap(HeaderElement::getName,HeaderElement::getValue,(a,b)->b));
// 等价于
Map<String, String> responseHeaders = Maps.newHashMap();
for (Header header : response.getAllHeaders()) {
HeaderElement[] headerElements = header.getElements();
if (Objects.nonNull(headerElements) && headerElements.length > 0) {
for (HeaderElement headerElement : headerElements) {
responseHeaders.put(headerElement.getName(), headerElement.getValue());
}
}
}
Stream.groupBy
// List按照某种关系聚合成Map<key,List>
Map<K, List<T>> groupListMap = orgList.stream()
.collect(Collectors.groupingBy(T -> return K));
Map<Long, List<OrderBO>> transferIdSubList = orderList.stream()
.collect(Collectors.groupingBy(bo -> bo.getBaseBO().getTransferId()));
Stream.reduce
https://blog.csdn.net/dalinsi/article/details/78093130
//集合元素求和
Integer sum = intList.stream().reduce(0, Integer::sum); // 0是初始值
Integer sum = intList.stream().reduce(0, (a,b)->a+b);
Integer sum = intList.stream().reduce(Integer::sum);
Integer sum = intList.stream().reduce((a,b)->a+b);
T reduce(T identity, BinaryOperator<T> accumulator);
/**
T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
*/
//求最大值,最小值
Optional<Integer> maxNum = intList.stream().reduce(Integer::max);
Optional<Integer> minNum = intList.stream().reduce(Integer::min);
//自定义比较符,两者比较返回seconds较大的节目实体
Optional<ShowBO> dstShow = showList.stream().reduce(
(showA, showB) -> showA.getSeconds() > showB.getSeconds() ? showA : showB);
Stream.sorted
// 按工资升序排序(自然排序)
List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
.collect(Collectors.toList());
// 按工资倒序排序
List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());
// 先按工资再按年龄升序排序
List<String> newList3 = personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName).collect(Collectors.toList());
// 先按工资再按年龄自定义排序(降序)
List<String> newList4 = personList.stream().sorted((p1, p2) -> {
if (p1.getSalary() == p2.getSalary()) {
return p2.getAge() - p1.getAge();
} else {
return p2.getSalary() - p1.getSalary();
}
}).map(Person::getName).collect(Collectors.toList());
URLUtils
public class URLUtil {
private static final Logger logger = LoggerFactory.getLogger(URLUtil.class);
public static String urlEncode(String source) {
return urlEncode(source, StandardCharsets.UTF_8.toString());
}
public static String urlEncode(String source, String charset) {
if (StringUtils.isBlank(source)) {
return source;
}
try {
return URLEncoder.encode(source, charset);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e, logger, null, "urlEncode不支持的编码", "source={0}, encodeCharset={1}", source, charset);
}
return null;
}
public static String urlDecode(String source) {
return urlDecode(source, StandardCharsets.UTF_8.toString());
}
public static String urlDecode(String source, String charset) {
if (StringUtils.isBlank(source)) {
return source;
}
try {
return URLDecoder.decode(source, charset);
} catch (Exception e) {
LogUtil.error(e, logger, null, "urlDecode解码失败", "source={0}, encodeCharset={1}", source, charset);
}
return null;
}
private static BitSet dontNeedEncoding;
static {
dontNeedEncoding = new BitSet(256);
int i;
for (i = 'a'; i <= 'z'; i++) {
dontNeedEncoding.set(i);
}
for (i = 'A'; i <= 'Z'; i++) {
dontNeedEncoding.set(i);
}
for (i = '0'; i <= '9'; i++) {
dontNeedEncoding.set(i);
}
//去掉+号的判断
//dontNeedEncoding.set('+');
/**
* 这里会有误差,比如输入一个字符串 123+456,它到底是原文就是123+456还是123 456做了urlEncode后的内容呢?<br>
* 其实问题是一样的,比如遇到123%2B456,它到底是原文即使如此,还是123+456 urlEncode后的呢? <br>
* 在这里,我认为只要符合urlEncode规范的,就当作已经urlEncode过了<br>
* 毕竟这个方法的初衷就是判断string是否urlEncode过<br>
*/
dontNeedEncoding.set('-');
dontNeedEncoding.set('_');
dontNeedEncoding.set('.');
dontNeedEncoding.set('*');
}
/**
* 判断str是否已经encode
* 经常遇到这样的情况,拿到一个URL,但是搞不清楚到底要不要encode.
* 不做encode吧,担心出错,做encode吧,又怕重复了
*/
public static boolean hasUrlEncoded(String source) {
/**
* 支持JAVA的URLEncoder.encode出来的string做判断。 即: 将' '转成'+' <br>
* 0-9a-zA-Z保留 <br>
* '-','_','.','*'保留 <br>
* 其他字符转成%XX的格式,X是16进制的大写字符,范围是[0-9A-F]
*/
boolean needEncode = false;
for (int i = 0; i < source.length(); i++) {
char c = source.charAt(i);
if (dontNeedEncoding.get((int) c)) {
continue;
}
if (c == '%' && (i + 2) < source.length()) {
// 判断是否符合urlEncode规范
char c1 = source.charAt(++i);
char c2 = source.charAt(++i);
if (isDigit16Char(c1) && isDigit16Char(c2)) {
continue;
}
}
// 其他字符,肯定需要urlEncode
needEncode = true;
break;
}
return !needEncode;
}
/**
* 判断c是否是16进制的字符
*
* @param c
* @return
*/
private static boolean isDigit16Char(char c) {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F');
}
}
HttpUtil
@Slf4j
@Component
public class HttpUtil {
private static final String SCHEME_HTTP = "http";
private static final String SCHEME_HTTPS = "https";
private static final int HTTP_PORT = 80;
private static final int HTTPS_PORT = 443;
private static final int DEFAULT_SOCKET_TIMEOUT = 1000;
private static final int DEFAULT_CONNECT_TIMEOUT = 2000;
private static final int DEFAULT_MAX_PER_ROUTE = 2;
private static final int DEFAULT_MAX_TOTAL = 500;
private static final int DEFAULT_TIME_TO_LIVE = 3000;
private static final int CONNECTION_IDLE_TIME_OUT = 3000;
// 连接池复用
private static volatile CloseableHttpClient reusableHttpClient;
private static ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("httpclient-watcher-schedule-pool-%d").daemon(true).build());
@PostConstruct
public void init() throws Exception {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new X509TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, new SecureRandom());
// HTTP连接在内部使用java.net.Socket对象来处理数据的传输,依赖于ConnectionSocketFactory接口来创建、初始化和连接套接字
// 创建套接字和将其连接到主机的过程是去耦合的,以便在连接操作中阻塞时可以关闭套接字,HttpClient用户能够在运行时提供应用程序特定的套接字初始化代码
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
// 创建和初始化普通(未加密)套接字的默认工厂
.register(SCHEME_HTTP, PlainConnectionSocketFactory.INSTANCE)
// 除了信任证书和在SSL/TLS协议级别上执行的客户端认证之外(SSL信任验证),一旦建立连接,HttpClient可以可选地验证目标主机名是否与服务器的X.509证书中存储的名称匹配(主机名验证),提供服务器信任资料的真实性的额外保证
// DefaultHostnameVerifier(默认): 符合RFC 2818.主机名必须与证书指定的任何备用名称匹配
// NoopHostnameVerifier: 主机名验证关闭,接受任何SSL会话为有效并与目标主机匹配
.register(SCHEME_HTTPS, new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE))
.build();
// Total time to live (TTL) set at construction time defines maximum life span of persistent connections regardless of their expiration setting.
// No persistent connection will be re-used past its TTL value. 默认2000ms
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
socketFactoryRegistry, null, null, null, DEFAULT_TIME_TO_LIVE, TimeUnit.MILLISECONDS);
// 每个域名设置单独的连接池数量
connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE);
// 连接池总数
connectionManager.setMaxTotal(DEFAULT_MAX_TOTAL);
connectionManager.setDefaultSocketConfig(SocketConfig.custom()
// 关闭Nagle算法(Nagle算法减少小包的数量)
.setTcpNoDelay(true)
.build());
RequestConfig defaultRequestConfig = RequestConfig.custom()
// 客户端发起TCP连接请求的超时时间
.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT)
// 客户端等待服务端返回数据的超时时间
.setSocketTimeout(DEFAULT_SOCKET_TIMEOUT)
.build();
reusableHttpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(defaultRequestConfig)
.build();
log.info("HttpUtil HttpClient init success!");
// 服务端假设关闭了连接,对客户端是不透明的(可能造成大量CLOSED_WAIT连接),
// HttpClient为了缓解这一问题,在某个连接使用前会检测这个连接是否过时,如果过时则连接失效,但是这种做法会为每个请求增加一定额外开销,
// 因此有一个定时任务专门回收长时间不活动而被判定为失效的连接,可以某种程度上解决这个问题
scheduler.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
/**
// 打印当前连接池状态
PoolStats totalStats = connectionManager.getTotalStats();
log.info("totalStats:" + totalStats);
*/
// 关闭失效连接并从连接池中移除
connectionManager.closeExpiredConnections();
// 关闭指定时间内不活动的连接并从连接池中移除,空闲时间从交还给连接管理器时开始
connectionManager.closeIdleConnections(CONNECTION_IDLE_TIME_OUT, TimeUnit.MILLISECONDS);
}
},
0, 10, TimeUnit.SECONDS);
} catch (Throwable e) {
log.error("HttpUtil HttpClient init error!", e);
throw e;
}
}
/**
* 单请求单通道
*/
public static CloseableHttpClient getDisposableHttpClient() {
return HttpClients.createDefault();
}
public static String get(String url, HttpHost target, HttpHost proxy, Map<String, String> headers, int socketTimeout, int connectTimeout) {
/**
* wrapHttpGet
*/
HttpGet request = new HttpGet(url);
if (MapUtils.isNotEmpty(headers)) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
request.setHeader(entry.getKey(), entry.getValue());
}
}
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(socketTimeout)
.setConnectTimeout(connectTimeout)
// 通过代理访问, proxy可传null
// 一般代理使用流程:proxy=new HttpHost("xxx.xxx.com", 80, "http")且headers包含指定的请求头
.setProxy(proxy)
.build();
request.setConfig(requestConfig);
return execute(request, target);
}
public static String post(String url, HttpHost target, HttpHost proxy, Map<String, String> headers,
ContentType contentType, Charset charset, Map<String, String> params, JSONObject postBody,
int socketTimeout, int connectTimeout) {
/**
* wrapHttpPost
*/
Assert.notNull(contentType, "contentType cannot be null.");
HttpPost request = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(socketTimeout)
.setConnectTimeout(connectTimeout)
.setProxy(proxy)
.build();
request.setConfig(requestConfig);
if (MapUtils.isNotEmpty(headers)) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
request.setHeader(entry.getKey(), entry.getValue());
}
}
if (ContentType.APPLICATION_JSON == contentType) {
Assert.notNull(postBody, "postBody cannot be null.");
StringEntity entity = new StringEntity(postBody.toJSONString(), ContentType.APPLICATION_JSON);
request.setEntity(entity);
} else if (ContentType.APPLICATION_FORM_URLENCODED == contentType) {
Assert.notNull(params, "params cannot be null.");
List<NameValuePair> pairs = new ArrayList<>();
for (String key : params.keySet()) {
pairs.add(new BasicNameValuePair(key, params.get(key)));
}
HttpEntity entity = new UrlEncodedFormEntity(pairs, charset);
request.setEntity(entity);
}
return execute(request, target);
}
public static String postJsonData(String url, Map<String, String> headers, JSONObject postBody) {
return post(url, null, null, headers, ContentType.APPLICATION_JSON, StandardCharsets.UTF_8,
null, postBody, DEFAULT_SOCKET_TIMEOUT, DEFAULT_CONNECT_TIMEOUT);
}
public static String postFormData(String url, Map<String, String> headers, Map<String, String> params) {
return post(url, null, null, headers, ContentType.APPLICATION_FORM_URLENCODED, StandardCharsets.UTF_8,
params, null, DEFAULT_SOCKET_TIMEOUT, DEFAULT_CONNECT_TIMEOUT);
}
public static String execute(HttpRequestBase request, HttpHost target) {
try {
// 访问发布该服务的特定IP target=new HttpHost("33.2.3.65", 80, "http")
HttpHost host = Objects.nonNull(target) ? target : URIUtils.extractHost(request.getURI());
return reusableHttpClient.execute(host, request, getStringResponseHandler());
} catch (ConnectTimeoutException | SocketTimeoutException e) {
log.error("httpUtil execute timeOut!", e);
} catch (Throwable e) {
log.error("httpUtil execute error!", e);
}
return null;
}
/**
* 从entity里解析data,response_header里的contentType为空时,默认采用UTF-8
*/
private static ResponseHandler<String> getStringResponseHandler() {
return httpResponse -> EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8);
}
/**
* 可添加入参控制handler行为
*/
private static ResponseHandler<Map<String, String>> getResponseHandler() {
return httpResponse -> {
Map<String, String> resultMap = Maps.newHashMap();
resultMap.put("httpStatusCode", String.valueOf(httpResponse.getStatusLine().getStatusCode()));
resultMap.put("content", EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8));
resultMap.put("byteLength", String.valueOf(IOUtils.toByteArray(httpResponse.getEntity().getContent()).length));
return resultMap;
};
}
}
标签:map,return,String,stream,List,static,常用工具
From: https://www.cnblogs.com/Red-Revolution/p/17184534.html