java?list和map切割分段的實(shí)現(xiàn)及多線程應(yīng)用案例
更新時(shí)間:2023年12月24日 15:08:04 作者:蠶1蠶2
這篇文章主要為大家介紹了java?list和map切割分段的實(shí)現(xiàn)及多線程應(yīng)用案例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
map工具類
/**
* @author chengxianwei
* @title map工具類
* @date 2023/08/03
**/
public class MapUtil {
/**
* List拆分 (指定分組大小)
*
* @param chunkList 被分隔的數(shù)組
* @param chunkNum 每段的大小
* @param <T> List中的類型
* @return
*/
public static <T> List<List<T>> listChunk(List<T> chunkList, int chunkNum) {
if (chunkList == null || chunkNum <= 0) {
List<List<T>> t = new ArrayList<>();
t.add(chunkList);
return t;
}
Iterator<T> iterator = chunkList.iterator();
int i = 1;
List<List<T>> total = new ArrayList<>();
List<T> tem = new ArrayList<>();
while (iterator.hasNext()) {
T next = iterator.next();
tem.add(next);
if (i == chunkNum) {
total.add(tem);
tem = new ArrayList<>();
i = 0;
}
i++;
}
if (!ChargeEmptyUtil.isEmpty(tem)) {
total.add(tem);
}
return total;
}
/**
* Map拆分 (指定分組大小)
*
* @param chunkMap 被切段的map
* @param chunkNum 每段的大小
* @param <k> map的key類型
* @param <v> map的value類型 如果是自定義類型,則必須實(shí)現(xiàn)equals和hashCode方法
* @return
*/
public static <k, v> List<Map<k, v>> mapChunk(Map<k, v> chunkMap, int chunkNum) {
if (chunkMap == null || chunkNum <= 0) {
List<Map<k, v>> list = new ArrayList<>();
list.add(chunkMap);
return list;
}
Set<k> keySet = chunkMap.keySet();
Iterator<k> iterator = keySet.iterator();
int i = 1;
List<Map<k, v>> total = new ArrayList<>();
Map<k, v> tem = new HashMap<>();
while (iterator.hasNext()) {
k next = iterator.next();
tem.put(next, chunkMap.get(next));
if (i == chunkNum) {
total.add(tem);
tem = new HashMap<>();
i = 0;
}
i++;
}
if (!ChargeEmptyUtil.isEmpty(tem)) {
total.add(tem);
}
return total;
}
/**
* Map拆分 (指定分組大小)
*
* @param map Map
* @param chunkSize 每個(gè)分組的大小 (>=1)
* @param <K> Key
* @param <V> Value
* @return 子Map列表
*/
public static <K, V> List<Map<K, V>> splitByChunkSize(Map<K, V> map, int chunkSize) {
if (Objects.isNull(map) || map.isEmpty() || chunkSize < 1) {
//空map或者分組大小<1,無(wú)法拆分
return Collections.emptyList();
}
int mapSize = map.size(); //鍵值對(duì)總數(shù)
int groupSize = mapSize / chunkSize + (mapSize % chunkSize == 0 ? 0 : 1); //計(jì)算分組個(gè)數(shù)
List<Map<K, V>> list = Lists.newArrayListWithCapacity(groupSize); //子Map列表
if (chunkSize >= mapSize) { //只能分1組的情況
list.add(map);
return list;
}
int count = 0; //每個(gè)分組的組內(nèi)計(jì)數(shù)
Map<K, V> subMap = Maps.newHashMapWithExpectedSize(chunkSize); //子Map
for (Map.Entry<K, V> entry : map.entrySet()) {
if (count < chunkSize) {
//給每個(gè)分組放chunkSize個(gè)鍵值對(duì),最后一個(gè)分組可能會(huì)裝不滿
subMap.put(entry.getKey(), entry.getValue());
count++; //組內(nèi)計(jì)數(shù)+1
} else {
//結(jié)束上一個(gè)分組
list.add(subMap); //當(dāng)前分組裝滿了->加入列表
//開始下一個(gè)分組
subMap = Maps.newHashMapWithExpectedSize(chunkSize); //新的分組
subMap.put(entry.getKey(), entry.getValue()); //添加當(dāng)前鍵值對(duì)
count = 1; //組內(nèi)計(jì)數(shù)重置為1
}
}
list.add(subMap); //添加最后一個(gè)分組
return list;
}
/**
* Map拆分(指定分組個(gè)數(shù))
*
* @param map Map
* @param groupSize 分組個(gè)數(shù) (>=1)
* @param <K> Key
* @param <V> Value
* @return 子Map列表
*/
public static <K, V> List<Map<K, V>> splitByGroupSize(Map<K, V> map, int groupSize) {
if (Objects.isNull(map) || map.isEmpty() || groupSize < 1) {
//空map或者分組數(shù)<1,無(wú)法拆分
return Collections.emptyList();
}
List<Map<K, V>> list = Lists.newArrayListWithCapacity(groupSize);
if (groupSize == 1) { //只有1個(gè)分組的情況
list.add(map);
return list;
}
int mapSize = map.size(); //鍵值對(duì)總數(shù)
int chunkIndex = 0; //當(dāng)前分組的下標(biāo),[0, groupSize-1]
int restCount = mapSize % groupSize; //平均后剩余的鍵值對(duì)數(shù)
int chunkSize0 = mapSize / groupSize; //每個(gè)分組鍵值對(duì)數(shù)量
int chunkSize1 = chunkSize0 + 1; //多分一個(gè)
int chunkSize = chunkIndex < restCount ? chunkSize1 : chunkSize0; //實(shí)際每組的大?。ㄇ懊娴牟糠址纸M可能會(huì)多分1個(gè))
int count = 0; //每個(gè)分組的組內(nèi)計(jì)數(shù)
Map<K, V> subMap = Maps.newHashMapWithExpectedSize(chunkSize);//子Map
for (Map.Entry<K, V> entry : map.entrySet()) {
if (count < chunkSize) {
//每個(gè)分組按實(shí)際分組大?。╟hunkSize)加入鍵值對(duì)
subMap.put(entry.getKey(), entry.getValue());
count++; //組內(nèi)計(jì)數(shù)+1
} else {
//結(jié)束上一個(gè)分組
list.add(subMap); //當(dāng)前分組裝滿了->加入列表
chunkIndex++; //分組個(gè)數(shù)+1
//開始下一個(gè)分組
chunkSize = chunkIndex < restCount ? chunkSize1 : chunkSize0; //重新計(jì)算分組大小
subMap = Maps.newHashMapWithExpectedSize(chunkSize); //新的分組
subMap.put(entry.getKey(), entry.getValue()); //添加當(dāng)前鍵值對(duì)
count = 1; //組內(nèi)計(jì)數(shù)重置為1
}
}
list.add(subMap); //添加最后一個(gè)分組
return list;
}
//測(cè)試
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("a", "1");
map.put("b", "2");
map.put("c", "3");
map.put("d", "4");
map.put("e", "5");
map.put("f", "6");
map.put("g", "7");
System.out.println("splitByChunkSize: " + splitByChunkSize(map, 3));
System.out.println("splitByGroupSize: " + splitByGroupSize(map, 3));
}
}應(yīng)用案例
List分段
{
// 導(dǎo)出動(dòng)態(tài)標(biāo)題
List<String> titleList = new ArrayList<>();
// 上網(wǎng)電量和發(fā)電量
String[] monthKeyArr = {"powernetday", "powerday"};
// 獲取導(dǎo)出電站基礎(chǔ)數(shù)據(jù)
List<XczxPoverPhotovolSourceVO>[0] = new ArrayList<>();
// 保存成<consNo, <日電量數(shù)據(jù)列表>> 形式
ConcurrentHashMap<String, List<XczxPoverPhotovolSourceDayEleVO>> xczxPovertyPowerDayListMap = new ConcurrentHashMap<>();
...
...
...
// 多線程分段批量處理List列表
int threadSize = 2000;//每2000條開啟一個(gè)任務(wù)
int dataSize = xczxPoverPhotovolSourceVOList[0].size();
int threadNum = dataSize / threadSize + 1;// 開啟的任務(wù)數(shù)
boolean special = dataSize % threadSize ==0;// 表計(jì)整除 (threadNum多算了一次)
Callable<Integer> task = null;
List<XczxPoverPhotovolSourceVO> curList = null;
List<Callable<Integer>> tasks = new ArrayList<>();
ExecutorService exec = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 使用Collections將線程不安全的集合轉(zhuǎn)成線程安全的集合
List<Map<String, Object>> dataList = Collections.synchronizedList(new ArrayList<>(dataSize));
for (int i = 0; i < threadNum; i++) {
if (i == threadNum - 1) {
if(special){
countDownLatch.countDown();
break;
}
curList = xczxPoverPhotovolSourceVOList[0].subList(threadSize * i, dataSize);
} else {
curList = xczxPoverPhotovolSourceVOList[0].subList(threadSize * i, threadSize * (i + 1));
}
final List<XczxPoverPhotovolSourceVO> list2 = curList;
task = new Callable<Integer>() {
@Override
public synchronized Integer call() throws Exception {
try {
XczxPoverPhotovolSourceVO next = null;
Date parallelInTime = null;
Map<String, Object> map = null;
List<Map<String, Object>> totalList = null;
Map<String, Object> totalMap = null;
List<Map<String, Object>> povertyList = null;
Map<String, Object> entityMap = null;
XczxPoverPhotovolSourceDayEleVO next2 = null;
XczxPoverPhotovolSourceDayEleVO curDayEle = null;
List<XczxPoverPhotovolSourceDayEleVO> list = null;
List<XczxPoverPhotovolSourceDayEleVO> listDay = new ArrayList<>();
// 迭代導(dǎo)出電站基礎(chǔ)數(shù)據(jù)
Iterator<XczxPoverPhotovolSourceVO> iterator = list2.iterator();
while (iterator.hasNext()) {
next = iterator.next();
map = CommonUtils.transClassToMapLowCase(next);
// 合計(jì) 集合
totalList = new ArrayList<>();
totalMap = new HashMap<>();
// 合計(jì)
BigDecimal powerNetTotal = new BigDecimal(BigInteger.ZERO);
BigDecimal powerTotal = new BigDecimal(BigInteger.ZERO);
// 指定日期的 戶號(hào)列表 17123
listDay = xczxPovertyPowerDayListMap.get(""+next.getId());
if (listDay != null && listDay.size() > 0) {
// 遍歷一個(gè)戶號(hào) 所有日期
for (int i = 0; i < titleList.size() - 1; i++) {
String date = titleList.get(i).substring(0, 4) + titleList.get(i).substring(5, 7) + titleList.get(i).substring(8, 10);
list = listDay.stream().filter(x -> date.equals(x.getPovertyDay())).collect(Collectors.toList());
povertyList = new ArrayList<>();
entityMap = new HashMap<>();
if (list != null && list.size() > 0) {
for (XczxPoverPhotovolSourceDayEleVO vo : list) {
entityMap.put(monthKeyArr[0] + titleList.get(i), new BigDecimal(vo.getPowerNetDay()));
entityMap.put(monthKeyArr[1] + titleList.get(i), new BigDecimal(vo.getPowerDay()));
powerNetTotal = powerNetTotal.add(new BigDecimal(vo.getPowerNetDay()));
powerTotal = powerTotal.add(new BigDecimal(vo.getPowerDay()));
povertyList.add(entityMap);
}
} else {
// 如果為空,填充0
entityMap.put(monthKeyArr[0] + titleList.get(i), "0");
entityMap.put(monthKeyArr[1] + titleList.get(i), "0");
povertyList.add(entityMap);
}
map.put(day + titleList.get(i), povertyList);
}
} else {
// 遍歷一個(gè)戶號(hào) 所有日期
for (int i = 0; i < titleList.size() - 1; i++) {
povertyList = new ArrayList<>();
entityMap = new HashMap<>();
// 如果為空,填充0
entityMap.put(monthKeyArr[0] + titleList.get(i), "0");
entityMap.put(monthKeyArr[1] + titleList.get(i), "0");
povertyList.add(entityMap);
povertyList.add(entityMap);
map.put(day + titleList.get(i), povertyList);
}
}
// 合計(jì)模型
totalMap.put(monthKeyArr[0] + "合計(jì)", powerNetTotal);
totalMap.put(monthKeyArr[1] + "合計(jì)", powerTotal);
totalList.add(totalMap);
map.put(day + "合計(jì)", totalList);
dataList.add(map);
map.put("xh", dataList.size());
}
} catch (Exception e) {
log.error("catch error",e);
} finally {
countDownLatch.countDown();
return 1;
}
}
};
tasks.add(task);
}
// 多線程并發(fā)
List<Future<Integer>> futureList = null;
try {
futureList = exec.invokeAll(tasks);
for(Future<Integer> future : futureList){
if(future!=null) {
while (!future.isDone());
}
}
countDownLatch.await();
} catch (Exception e) {
log.error("catch error",e);
} finally {
exec.shutdown();
}
...
...
...
}Map分段
{
...
...
...
// <戶主身份證號(hào), List<戶主成員(含戶主)>> 集合檔案信息
Map<String, List<XczxArchives>> impListMap = new HashMap<>();
...
...
...
// 多線程分段批量處理Map集合
int threadSize = 2000;// 每2000條開啟一個(gè)任務(wù)
int dataSize = impListMap.size();// map數(shù)據(jù)條數(shù)
int threadNum = dataSize / threadSize + 1;// 開啟的任務(wù)數(shù)
List<Callable<Integer>> tasks = new ArrayList<>();
ExecutorService exec = Executors.newCachedThreadPool();
List<Map<String, List<XczxArchives>>> splitGroup = MapUtil.splitByChunkSize(impListMap, threadSize);
for (int i = 0; i < threadNum; i++) {
Map<String, List<XczxArchives>> curLists = splitGroup.get(i);
// 新建一個(gè)并發(fā)任務(wù)
Callable<Integer> task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int count = 0;
// iterator遍歷分段Map
Iterator<Map.Entry<String, List<XczxArchives>>> entryIterator = curLists.entrySet().iterator();
while (entryIterator.hasNext()) {
XczxArchives head = null;
Map.Entry<String, List<XczxArchives>> entry = entryIterator.next();
String key = entry.getKey();// 戶主身份證號(hào)
List<XczxArchives> list = entry.getValue();// 戶主及成員列表
Iterator<XczxArchives> dataIterator = list.iterator();
while (dataIterator.hasNext()) {
XczxArchives next = dataIterator.next();
if (key.equals(next.getIdCard())) {
head = next;
dataIterator.remove();
}
}
if (head != null) {
// 插入戶主
head.setHeadName(null);
head.setHeadIdCard(null);
head.setHeadMobile(null);
head.setCreateTime(date);
head.setUpdateTime(date);
int result = xczxArchivesMapper.insert(head);
if (result == 1) {
count++;
if (list != null && list.size() > 0) {
for (XczxArchives next : list) {
next.setCreateTime(date);
next.setUpdateTime(date);
next.setHeadId(head.getArchivesId());
}
// 插入成員
int result2 = xczxArchivesMapper.batchInsert(list);
if (result2 > 0) {
count = count + result2;
}
}
}
}
}
return count;
}
};
tasks.add(task);
}
// 多線程并發(fā)
List<Future<Integer>> futureList = null;
try {
futureList = exec.invokeAll(tasks);
for (Future<Integer> future : futureList) {
if (future != null) {
while (!future.isDone());
successCount = successCount + future.get();
}
}
} catch (Exception e) {
log.error("catch error",e);
} finally {
exec.shutdown();
}
...
...
...
}以上就是java list和map切割分段的實(shí)現(xiàn)及多線程應(yīng)用案例的詳細(xì)內(nèi)容,更多關(guān)于java list map多線程切割分段的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java阻塞隊(duì)列中的BlockingQueue接口詳解
這篇文章主要介紹了Java阻塞隊(duì)列中的BlockingQueue接口詳解,對(duì)于Queue而言,BlockingQueue是主要的線程安全的版本,具有阻塞功能,可以允許添加、刪除元素被阻塞,直到成功為止,BlockingQueue相對(duì)于Queue而言增加了兩個(gè)方法put、take元素,需要的朋友可以參考下2023-09-09
Java IO文件編碼轉(zhuǎn)換實(shí)現(xiàn)代碼
這篇文章主要介紹了Java IO文件編碼轉(zhuǎn)換實(shí)現(xiàn)代碼,有需要的朋友可以參考一下2013-12-12
SpringBoot 整合 RabbitMQ 的使用方式(代碼示例)
本文詳細(xì)介紹了使用RabbitTemplate進(jìn)行消息傳遞的幾種模式,包括點(diǎn)對(duì)點(diǎn)通信、發(fā)布/訂閱模式、工作隊(duì)列模式、路由模式和主題模式,每種模式都通過代碼示例展示了生產(chǎn)者和消費(fèi)者的實(shí)現(xiàn),幫助開發(fā)者理解和運(yùn)用RabbitMQ進(jìn)行高效的消息處理2024-10-10
SpringBoot2整合Redis實(shí)現(xiàn)讀寫操作
Redis,對(duì)于大家來說應(yīng)該不陌生,是經(jīng)常使用的開發(fā)技術(shù)之一。本文將結(jié)合實(shí)例代碼,介紹SpringBoot2整合Redis實(shí)現(xiàn)讀寫操作,感興趣的小伙伴們可以參考一下2021-07-07
Java的MyBatis框架中MyBatis Generator代碼生成器的用法
這篇文章主要介紹了Java的MyBatis框架中Mybatis Generator代碼生成器的用法,Mybatis Generator主要被用來生成繁瑣的配置文件來提高效率,需要的朋友可以參考下2016-04-04
詳解SpringBoot中Session超時(shí)原理說明
本篇文章主要介紹了詳解SpringBoot中Session超時(shí)原理說明,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08

