1、初始化一个对象的简便方式
Student stu = new Student(){{
setName("张三");
}};
2、map后去重
List<String> names = list.stream().map(e -> e.getName()).distinct().collect(Collectors.toList());
3、根据某一属性对集合去重
list = list.stream()
.collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
new TreeSet<>(Comparator.comparing(e -> e.getName()))), ArrayList::new));
4、集合排序
// 正序
list = list.stream().sorted((a, b) -> a.getName().compareTo(b.getName())).collect(Collectors.toList());
list = list.stream().sorted(Comparator.comparing(e -> e.getName())).collect(Collectors.toList());
min = list.stream().sorted(Comparator.comparing(e -> e.getName())).findFirst().orElse(null);
// 倒序
list = list.stream().sorted((a, b) -> -(a.getName().compareTo(b.getName()))).collect(Collectors.toList());
list = list.stream().sorted(Comparator.comparing(e -> e.getName()).reversed()).collect(Collectors.toList());
max = list.stream().sorted(Comparator.comparing(e -> e.getName()).reversed()).findFirst().orElse(null);
5、过滤
boolean exists = list.stream().anyMatch(e -> e.getAmount() != null && e.getAmount().compareTo(BigDecimal.ZERO)> 0);
6、集合转换为数组
Long[] ids = list.toArray(new Long[]{});
Long[] ocrFileIds = rows.stream().toArray(Long[] :: new);
7、获取Sum、Count、Max、Min
BigDecimal sum1 = list.stream().map(e -> e.getScore()).reduce((a,b) -> a.add(b)).orElse(BigDecimal.ZERO);
BigDecimal sum2 = list.stream().map(e -> e.getScore()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
long count = list.stream().filter(e -> e.getAmount() != null && e.getAmount().compareTo(BigDecimal.ZERO)> 0).count();
Student max = deleteList.stream().max((a, b) -> a.getAmount().compareTo(b.getAmount())).orElse(null);
Student min = deleteList.stream().min((a, b) -> a.getAmount().compareTo(b.getAmount())).orElse(null);
8、Group By 分组
Map<String,List<Student>> map = list.stream().collect(Collectors.groupingBy(e -> e.getName()));
Map<String, Long> collect1 = list.stream().collect(Collectors.groupingBy(e -> e.getName(), Collectors.summingDouble(e -> e.getAmount())));
Map<String, Double> collect2 = list.stream().collect(Collectors.groupingBy(e -> e.getName(), Collectors.averagingDouble(e -> e.getAmount())));
Map<String, Long> collect3 = list.stream().collect(Collectors.groupingBy(e -> e.getName(), Collectors.counting()));
Map<String, Optional<ExpenseInvoice>> collect4 = list.stream().collect(Collectors.groupingBy(e -> e.getName(), Collectors.maxBy((a, b) -> a.getAmount().compareTo(b.getAmount()))));
Map<String, Optional<ExpenseInvoice>> collect5 = list.stream().collect(Collectors.groupingBy(e -> e.getName(), Collectors.minBy((a, b) -> a.getAmount().compareTo(b.getAmount()))));
Map<String, DoubleSummaryStatistics> collect = list.stream().collect(Collectors.groupingBy(e -> e.getName(), Collectors.summarizingDouble(e -> e.getAmount())));
for (Map.Entry<String, DoubleSummaryStatistics> entry : collect.entrySet()) {
double sum = entry.getValue().getSum();
double average = entry.getValue().getAverage();
long count = entry.getValue().getCount();
double max = entry.getValue().getMax();
double min = entry.getValue().getMin();
}
9、使用CountDownLatch同时跑多个任务,一起返回
List<File> countFileList = new ArrayList<>();
if (CollectionUtils.isEmpty(countFileList)) {
return;
}
// 定义线程安全的结果集
List<File> files = Collections.synchronizedList(new ArrayList<>());
// 定义CountDownLatch线程数,有多少个请求,我们就定义多少个
CountDownLatch runningThreadNum = new CountDownLatch(countFileList.size());
for (File file : countFileList) {
countDownLatchTaskService.getFileBytes(runningThreadNum, file, files);
}
try {
// 调用CountDownLatch的await方法则当前主线程会等待,直到CountDownLatch类型的runningThreadNum清0
// 每个任务处理完成会对runningThreadNum减1
// 如果等待?分钟后当前主线程都等不到runningThreadNum清0,则认为超时,直接中断,抛出中断异常InterruptedException
runningThreadNum.await(wait, timeUnit);
} catch (InterruptedException e) {
// 非正常中断应该抛出异常或返回错误结果
logger.error("CountDownLatch识别线程超时", e);
throw new ApplicationException(ResponseCode.OVERTIME_FILE_ERROR);
}
// CountDownLatchTaskService.java 核心代码
/**
* 获取文件数据
* @param runningThreadNum 正在运行的线程
* @param file 识别文件
* @param resultList 识别结果列表
*/
@Async("fileBytesExecutor")
public void getFileBytes(CountDownLatch runningThreadNum, File file, List<File> resultList) {
try{
// 如果Bytes为空,就自己去取
CommonService.getFileBytes(file);
if (file.getSize() <= 0 || ArrayUtils.isEmpty(file.getBytes()) || StringUtils.isEmpty(file.getUrl())) {
logger.info("文件不存在或文件大小为空或文件Url为空, fileId:::{}, url:::{}, fileSize:::{}", file.getId(), file.getUrl(), file.getSize());
return;
}
resultList.add(file);
} catch(Exception e) {
logger.error("获取文件数据出错", e);
} finally {
// 当前线程处理完成,runningThreadNum线程数减1,此操作必须在finally中完成,避免处理异常后造成runningThreadNum线程数无法清0
runningThreadNum.countDown();
}
}
10、异步任务等待当前事务提交后再执行
// 等待当前事务提交后再处理异步业务,防止数据还未提交导致数据有误
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 异步任务执行
doAsyncWork();
}
});
11、多线程环境下获取时间戳踩坑
在多线程代码中调用System.currentTimeMillis()耗时极慢
可见,并发调用System.currentTimeMillis()一百次,耗费的时间是单线程调用一百次的250倍。如果单线程的调用频次增加(比如达到每毫秒数次的地步),也会观察到类似的情况。实际上在极端情况下,System.currentTimeMillis()的耗时甚至会比创建一个简单的对象实例还要多,看官可以自行将上面线程中的语句换成new HashMap<>之类的试试看。
解决方案:
- 策略一:如果对时间精确度要求不高的话可以使用独立线程缓存时间戳。
- 策略二:使用Linux的clock_gettime()方法。
策略一实现代码:
public class CurrentTimeMillisClock {
private volatile long now;
private CurrentTimeMillisClock() {
this.now = System.currentTimeMillis();
scheduleTick();
}
private void scheduleTick() {
new ScheduledThreadPoolExecutor(1, runnable -> {
Thread thread = new Thread(runnable, "current-time-millis");
thread.setDaemon(true);
return thread;
}).scheduleAtFixedRate(() -> {
now = System.currentTimeMillis();
}, 1, 1, TimeUnit.MILLISECONDS);
}
public long now() {
return now;
}
public static CurrentTimeMillisClock getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final CurrentTimeMillisClock INSTANCE = new CurrentTimeMillisClock();
}
}