避免使用Null值
在程序开发中,特别是涉及到集合的场景,90%都不允许null值的存在,特别是null值存在歧义,当使用ConcurrentMap是为了并发安全不允许将null值作为key,但是普通map允许null值为key,当null作为key时它与普通的key类似,多次put会覆盖,二次hash计算的桶默认为0。当null作为value时,当我们拿到一个value为null,我们无法判断是“不存在这个key”还是“这个key对应的值为null”。
使用Optional 可选在对集合进行处理解决掉空集合的情况
//提供默认值 String result = Optional.fromNullable(getValue()) .or("默认值"); // 返回Optional而不是直接返回可能为null的对象 public Optional<User> findUserById(Long id) { User user = userRepository.findById(id); return Optional.fromNullable(user); } // 方法链式调用 public String getUserEmail(Long userId) { return Optional.fromNullable(findUserById(userId)) .transform(User::getEmail) .or("未找到邮箱"); } // 条件判断和默认值处理 public Address getShippingAddress(Order order) { return Optional.fromNullable(order) .transform(Order::getShippingAddress) .or(new Address("默认地址")); }
先决条件 Preconditions
可以作为JSR提供的参数校验替代(@Value、@NotBlank、@NotEmpty、@NotBlank)
结合ControllerAdvice统一异常处理进行友好的错误抛出
Objects工具类
对Object类的常见方法进行了工具类封装。
Objects.equal(a,b)
Objects.hashCode(a,b,c...)
Objects.toString(a)
Objects.compareTo(a,b)
相比于Guava提供的Objects类,我们现在更喜欢使用JDK提供的Objects类,功能几乎一样
Collections集合
Immutable Collections不可变集合
-
线程安全
-
一但创建就不能再变
-
由于不需要变化,它比可变集合更节省内存
-
可以作为常数使用
创建:
//of ImmutableSet.of("a", "b", "c", "a", "d", "b"); //copyOf or asList void thingamajig(Collection<String> collection) { ImmutableList<String> defensiveCopy = ImmutableList.copyOf(collection); ... } //builder() public static final ImmutableSet<Color> GOOGLE_COLORS = ImmutableSet.<Color>builder() .addAll(WEBSAFE_COLORS) .add(new Color(0, 191, 255)) .build();
Multiset 多集合
一般用于计数
取代了传统的Map+for循环进行元素计数的方式。
Multiset与Map在功能上有相似的地方,但是本质上只是Multiset实现了Map的功能,Multiset仍然是真正的Collection类型
Multimap多地图
实现了一对多的映射(处理一个键对多个值的映射数据结构)
a -> 1 a -> 2 a -> 4 b -> 3 c -> 5
例如当我们在做图论的编程题时经常遇到对点的存储,Map<K, List<V>>或
Map<K, Set<V>>,使用Multimap可以取代这种结构
// 创建 Multimap Multimap<String, String> multimap = ArrayListMultimap.create(); // 添加键值对 multimap.put("fruits", "apple"); multimap.put("fruits", "banana"); multimap.put("fruits", "orange"); multimap.put("vegetables", "carrot"); multimap.put("vegetables", "potato"); // 获取值 Collection<String> fruits = multimap.get("fruits"); // 输出: [apple, banana, orange] // 检查特定键值对是否存在 boolean hasApple = multimap.containsEntry("fruits", "apple"); // true // 移除键值对 multimap.remove("fruits", "apple"); // 获取所有条目数 int size = multimap.size(); // 所有键值对的总数 // 获取不同键的数量 int keySize = multimap.keySet().size();
BiMap 一一映射
当我们既需要键映射到值又需要值映射回键时,采用BiMap取代Map<K,V>+Map<V,K>
BiMap要求键和值都是唯一的,如果试图把键映射到已存在的值上会抛出IllegalArgumentException
//BiMap<K,V>正常情况下可以当作Map<K,V>使用,但是值要求唯一 //使用inverse()方法查看逆值 BiMap<String, Integer> userId = HashBiMap.create(); ... String userForId = userId.inverse().get(id);
Table表格
表格类型,取代了Map<R,Map<C,V>>
有类似于excel的行列概念
Table<Vertex, Vertex, Double> weightedGraph = HashBasedTable.create(); weightedGraph.put(v1, v2, 4); weightedGraph.put(v1, v3, 20); weightedGraph.put(v2, v3, 5); weightedGraph.row(v1); // returns a Map mapping v2 to 4, v3 to 20 weightedGraph.column(v3); // returns a Map mapping v1 to 20, v2 to 5 //使用案例 // 创建 Table Table<String, String, Integer> table = HashBasedTable.create(); // 添加数据 table.put("张三", "语文", 90); table.put("张三", "数学", 95); table.put("李四", "语文", 85); table.put("李四", "数学", 88); // 获取特定值 Integer score = table.get("张三", "语文"); // 90 // 检查是否包含特定单元格 boolean hasScore = table.contains("张三", "语文"); // true // 删除数据 table.remove("张三", "语文"); // 获取表格大小 int size = table.size(); // 所有单元格的数量
Caches 缓存
缓存: 利用空间换时间的思想,将一些较为常见的查询数据进行本地保存,优化查询速度,减轻mysql与redis等存储的压力。
如果你在项目中想使用到本地缓存,一种方式是使用线程安全的ConcurrentHashMap
,但是使用Cache可以实现ConcurrentHashMap的所有功能,还提供了缓存所需要的内存限制、时间限制、淘汰策略、移除监听等
搭配Spring完整使用例子:
@Configuration @EnableCaching public class GuavaCacheConfig { @Value("${cache.user.maximum-size:1000}") private long maximumSize; @Value("${cache.user.expire-after-write:30}") private long expireAfterWrite; @Value("${cache.user.expire-after-access:15}") private long expireAfterAccess; @Bean public Cache<String, User> userCache() { CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder() // 基本配置 .maximumSize(maximumSize) // 最大缓存条目数 .expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES) // 写入后过期时间 .expireAfterAccess(expireAfterAccess, TimeUnit.MINUTES) // 访问后过期时间 // 高级配置 .concurrencyLevel(4) // 并发级别 .initialCapacity(100) // 初始容量 .softValues() // 使用软引用存储值 // 统计和监控 .recordStats() // 开启统计 .removalListener(notification -> { log.info("Cache removal - Key: {}, Value: {}, Cause: {}", notification.getKey(), notification.getValue(), notification.getCause()); }); return cacheBuilder.build(); } // 用于监控的 Metrics @Bean public CacheMetricsManager cacheMetricsManager(Cache<String, User> userCache) { return new CacheMetricsManager(userCache); } } @Component @Slf4j class CacheMetricsManager { private final Cache<String, User> cache; public CacheMetricsManager(Cache<String, User> cache) { this.cache = cache; } @Scheduled(fixedRate = 300000) // 每5分钟执行一次 public void logCacheMetrics() { CacheStats stats = cache.stats(); log.info("Cache Stats - Hits: {}, Misses: {}, Hit Rate: {}", stats.hitCount(), stats.missCount(), stats.hitRate()); } }