Collections.unmodifiableMap

本文详细介绍了Java中Collections.unmodifiableMap方法的原理及应用。通过示例代码展示了如何创建不可变Map及其限制,并讨论了如何确保线程安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Collections.unmodifiableMap 是什么?

Java的官方解释:

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K,? extends V> m)
Returns an unmodifiable view of the specified map. This method allows modules to provide users with "read-only" access to internal maps. Query operations on the returned map "read through" to the specified map, and attempts to modify the returned map, whether direct or via its collection views, result in an  UnsupportedOperationException.

翻译过来就是:该方法返回了一个map的不可修改的视图umap, 为用户提供了一种生成只读容器的方法。如果尝试修改该容器umap, 将会抛出UnsupportedOperationException异常。

2. Collections.unmodifiableMap 能做什么?

在《重构-改善既有代码逻辑》一书中提到了封装集合的功能(Encapsulate Collection)。

我们在类中经常需要返回一个集合,比如mapA。如果直接返回成员变量mapA本身的话,相当于对外暴露了集合的引用,外部就可以随意修改该对象的集合,该对象可能对修改都一无所知,属性却发生了变化。

一种解决方法,就是将该集合修饰为private, 在返回集合的方法中采用Collections.unmodifiableMap(mapA),返回mapA的一个不可变的副本。且该方法要比我们自己去复制一个副本效率要高。

3. Collections.unmodifiableMap 构造的map真的不可修改吗?

遗憾的是该结论并不总是成立。对于map<key, value>中的内容value, unmodifiableMap仅仅保证的是它的引用不能被修改,如果value对应的是一个可变对象,那么该unmodifiableMap的内容还是可变的。见实例:

 

 1 public class UnmodifiableMap {
 2 
 3     public static void main(String[] args) {
 4 
 5         Map<String, Student> map = new HashMap<String, Student>();
 6         Student tom = new Student("tom", 3);
 7         map.put("tom", tom);
 8         map.put("jerry", new Student("jerry", 1));
 9 
10         Map<String, Student> unmodifiableMap = Collections.unmodifiableMap(map);
11         // unmodifiableMap.put("tom", new Student("tom", 11));  // tag1
12         tom.setAge(11); // tag2
13         System.out.println(unmodifiableMap);
14     }
15 
16 }
17 
18 // mutable
19 class Student {
20     private String name;
21     private int age;
22 
23     public Student(String name, int age) {
24         this.name = name;
25         this.age = age;
26     }
27 
28     public String getName() {
29         return name;
30     }
31 
32     public void setName(String name) {
33         this.name = name;
34     }
35 
36     public int getAge() {
37         return age;
38     }
39 
40     public void setAge(int age) {
41         this.age = age;
42     }
43 
44     @Override
45     public String toString() {
46         return "Student{" +
47                 "name='" + name + '\'' +
48                 ", age=" + age +
49                 '}';
50     }
51 }

 

代码中Student 对象是可变对象。在tag1处,尝试更换为另一个对象,引用发生了变化,会抛出UnsupportedOperationException异常。unmodifiableMap阻止了对其的修改

但如果引用不变,见tag2处,还是tom, 但对该对象内容作了修改,unmodifiableMap并未阻止该行为。unmodifiableMap的内容变为了

{jerry=Student{name='jerry', age=1}, tom=Student{name='tom', age=11}}

所以为了线程安全,在使用Collections.unmodifiableMap的同时,尽量让其中的内容实现为不可变对象。

 

 

转载于:https://www.cnblogs.com/dreamysmurf/p/6253737.html

@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); // 自定义认证提供者 AuthenticationProvider paramValidationProvider = new AuthenticationProvider() { @Override public Authentication authenticate(Authentication authentication) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 仅对特定请求进行参数校验 if ("/oauth/app/verifyCode/token".equals(request.getRequestURI()) && "POST".equalsIgnoreCase(request.getMethod())) { List<String> errors = new ArrayList<>(); Map<String, String> paramRules = Map.of( "phoneOrAccount", "账号不能为空", "accountType", "账号类型不能为空", "deviceCode", "机器码不能为空", "code", "图形验证码不能为空", "verifyCode", "短信验证码不能为空" ); // 执行参数校验 paramRules.forEach((param, errorMsg) -> { if (isEmpty(request.getParameter(param))) { errors.add(errorMsg); } }); // 抛出参数校验异常 if (!errors.isEmpty()) { throw new BadCredentialsException(String.join("; ", errors)); } } return authentication; } @Override public boolean supports(Class<?> authentication) { return true; // 支持所有认证类型 } }; http.authenticationProvider(paramValidationProvider) .authorizeRequests() .anyRequest().authenticated() .and() .exceptionHandling() .authenticationEntryPoint((request, response, authException) -> { // 处理参数校验异常 if (authException instanceof BadCredentialsException) { response.setStatus(HttpStatus.BAD_REQUEST.value()); response.getWriter().write(authException.getMessage()); } else { // 其他认证异常处理 response.sendError(HttpStatus.UNAUTHORIZED.value(), "未认证"); } }); // 表单登录配置保持不变 http.formLogin() .loginProcessingUrl("/user/login") .successHandler(authenticationSuccessHandler) .failureHandler(authenticationFailureHandler) .permitAll() .and() .logout() .logoutSuccessUrl("/login.html") .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()) .addLogoutHandler(oauthLogoutHandler) .clearAuthentication(true); // 应用自定义安全配置 http.apply(validateCodeSecurityConfig) .and() .apply(phoneOrAccountAuthenticationSecurityConfig) .and() .apply(unionIdAuthenticationSecurityConfig); // 解决不允许显示在iframe的问题 http.headers().frameOptions().disable(); http.headers().cacheControl(); // 对所有请求使用无状态会话策略 http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } // 辅助方法 private boolean isEmpty(String value) { return value == null || value.trim().isEmpty(); } 结合// 在类中声明静态常量 private static final Map<String, String> PARAM_RULES; static { Map<String, String> map = new HashMap<>(); map.put("phoneOrAccount", "账号不能为空"); map.put("accountType", "账号类型不能为空"); map.put("deviceCode", "机器码不能为空"); map.put("code", "图形验证码不能为空"); map.put("verifyCode", "短信验证码不能为空"); PARAM_RULES = Collections.unmodifiableMap(map); } // 在方法中使用 List<String> errors = new ArrayList<>(); PARAM_RULES.forEach((param, errorMsg) -> { if (isEmpty(request.getParameter(param))) { errors.add(errorMsg); } }); 进行修改
06-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值