Java集合中的List、Set、Map在企业级开发中被频繁使用,根据二八法则,我们可以先掌握这3种数据结构的高频使用场景和常见的使用注意事项,在此基础上再去拓展延伸,这样工作起来才事半功倍。
1. List
1.1. 使用场景
工作中常用的List实现是ArrayList和LinkedList,一般数据库查询、上下游对接传参等场景会涉及到它。
代码样例:
package org.example;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* List使用场景样例
*/
public class ListUseScene {
private void example(List<String> paramList) {
// 数据库查询多条记录返回List
List<Object> userList = this.queryUserList();
// 方法出、入参使用List
List<String> result = this.bizProcess(paramList);
// 遍历某个数据结构生成一个新的List, 比如遍历Map过滤出用户名为Sam开头的userList
Map<String, Object> userNameMap = new HashMap<>(16);
List<Object> userNameStartWithSam = userNameMap.entrySet().stream().filter(item -> item.getKey().startsWith("Sam")).collect(Collectors.toList());
}
private List<String> bizProcess(List<String> paramList) {
// 模拟接收外部List入参进行业务逻辑处理
return Collections.emptyList();
}
private List<Object> queryUserList() {
// 模拟从db查询用户列表
return Collections.emptyList();
}
}
1.2. 使用注意事项
企业一般都有自己的编程规范,遵循规范有助于统一团队编码风格和避免掉坑。比如使用List时要指定初始容量、遍历删除使用迭代器模式等。
代码样例:
package org.example;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* List使用注意事项举例
*/
public class ListUseCaution {
// 避免全局变量List存储过多元素
private static final List<String> SYS_SETTING_CACHE = new LinkedList<>();
// 并发修改场景使用JUC包下的线程安全集合
private static final List<String> COPY_ON_WRITE_LIST = new CopyOnWriteArrayList<>();
private void example(List<String> paramList) {
// 选择合适的List实现, 初始化时指定容量
List<String> list = new ArrayList<>(10);
// 遍历删除使用迭代器, 比如遍历移除用户名为sam的元素
List<String> userNameList = new LinkedList<>();
userNameList.removeIf("sam"::equals);
// 谨慎修改方法入参传入的List
this.methodA(paramList);
this.methodB(paramList);
}
private void methodA(List<String> paramList) {
// 如果后面其他逻辑需要使用原始的入参变量,那么methodA中应该只用于查询,修改应该copy一个副本
List<String> paramListCopy = new LinkedList<>(paramList);
}
private void methodB(List<String> paramList) {
// 需要使用原始的paramList进行业务逻辑判断,methodA中不能对其修改
}
}
2. Map
2.1. 使用场景
代码样例:
package org.example;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Map使用场景样例
*/
public class MapUseScene {
private void example() {
// 存储某种映射关系, 比如商品sku编码跟商品sku实体的映射
Map<String, Sku> skuCodeMapExample = new HashMap<>(16);
// List转Map, 比如将商品sku List转为Map{sku编码-sku实体}用于业务逻辑处理时根据编码获取sku信息
List<Sku> skuList = new LinkedList<>();
Map<String, Sku> skuCodeMap = skuList.stream().collect(Collectors.toMap(Sku::getSkuCode, Function.identity(), (v1, v2) -> v1));
List<OrderItem> orderItemList = new LinkedList<>();
for (OrderItem orderItem : orderItemList) {
String skuCode = orderItem.getSkuCode();
// 模拟遍历订单明细时根据sku编码获取sku信息进行业务逻辑处理
Sku sku = skuCodeMap.get(skuCode);
}
// 对List进行分组转为Map, 比如根据spu编码对sku进行分组,属于同一spu的归为一组用于后续业务处理
List<Sku> skuInfoList = new LinkedList<>();
Map<String, List<Sku>> sameSpuMap = skuInfoList.stream().collect(Collectors.groupingBy(Sku::getSpuCode));
sameSpuMap.forEach((spuCode, tmpSkuList) -> {
// 模拟计算同一spu下的所有sku价格之和
BigDecimal sameSpuTotalPrice = tmpSkuList.stream().map(Sku::getSkuPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
});
}
private static final class Sku {
private String spuCode;
private String spuName;
private String skuCode;
private String skuName;
private BigDecimal skuPrice;
public String getSpuCode() {
return spuCode;
}
public void setSpuCode(String spuCode) {
this.spuCode = spuCode;
}
public String getSpuName() {
return spuName;
}
public void setSpuName(String spuName) {
this.spuName = spuName;
}
public String getSkuCode() {
return skuCode;
}
public void setSkuCode(String skuCode) {
this.skuCode = skuCode;
}
public String getSkuName() {
return skuName;
}
public void setSkuName(String skuName) {
this.skuName = skuName;
}
public BigDecimal getSkuPrice() {
return skuPrice;
}
public void setSkuPrice(BigDecimal skuPrice) {
this.skuPrice = skuPrice;
}
}
private final class OrderItem {
private String orderItemId;
private String skuCode;
public String getOrderItemId() {
return orderItemId;
}
public void setOrderItemId(String orderItemId) {
this.orderItemId = orderItemId;
}
public String getSkuCode() {
return skuCode;
}
public void setSkuCode(String skuCode) {
this.skuCode = skuCode;
}
}
}
2.2. 使用注意事项
代码样例:
package org.example;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Map使用注意事项举例
*/
public class MapUseCaution {
// 并发修改场景使用JUC包下的线程安全类
private static final Map<String, String> CONCURRENT_HASH_MAP = new ConcurrentHashMap<>(16);
private void example(){
// 初始化时指定容量
Map<String, Object> mapWithInitialCapacity = new HashMap<>(16);
}
}
3. Set
3.1. 使用场景
Set在业务开发中的使用频率相比List和Map而言没有那么高,主要用于去重相关的场景。
代码样例:
package org.example;
import java.math.BigDecimal;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Set使用场景样例
*/
public class SetUseScene {
private void example() {
// 去重, 比如上游传入的订单集合包含用户id,需要提取出去重的用户id查询用户信息
List<Order> orderList = new LinkedList<>();
Set<String> userIdSet = orderList.stream().map(Order::getUserId).collect(Collectors.toSet());
this.queryUserByIds(userIdSet);
}
private void queryUserByIds(Set<String> userIdSet) {
// 模拟根据id查询用户信息
}
private final class Order {
private String id;
private String userId;
private BigDecimal totalAmt;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public BigDecimal getTotalAmt() {
return totalAmt;
}
public void setTotalAmt(BigDecimal totalAmt) {
this.totalAmt = totalAmt;
}
}
}
3.2. 使用注意事项
代码样例:
package org.example;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Map使用注意事项举例
*/
public class SetUseCaution {
// 并发修改场景使用JUC包下的线程安全类
private static final Set<String> COPY_ON_WRITE_ARRAY_SET = new CopyOnWriteArraySet<>();
private void example(){
// 初始化时指定容量
Set<String> setWithInitialCapacity = new HashSet<>(16);
}
}