Collections.unmodifiableMap

探讨了Java中Collections.unmodifiableMap的特性,指出虽然该方法创建的Map不能通过put等方法修改,但可以通过修改已存在的值对象来间接变更Map的内容。

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

对于不可变的Collections.unmodifiableMap 是否真的不可以变?

博客:http://blog.youkuaiyun.com/l2tp1012/article/details/39338209


 Java Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package com.example.demo;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class SeeminglyUnmodifiable
{
    private Map<String, Point> startingLocations = new HashMap<String, Point>(3);

    public SeeminglyUnmodifiable()
    {
        startingLocations.put("LeftRook"new Point(11));
        startingLocations.put("LeftKnight"new Point(12));
        startingLocations.put("LeftCamel"new Point(13));
        //..more locations..
    }

    public Map<String, Point> getStartingLocations()
    {
        return Collections.unmodifiableMap(startingLocations);
    }

    public static void main(String [] args)
    {
        SeeminglyUnmodifiable  pieceLocations = new SeeminglyUnmodifiable();
        Map<String, Point> locations = pieceLocations.getStartingLocations();

        Point camelLoc = locations.get("LeftCamel");
        System.out.println("The LeftCamel's start is at [ " + camelLoc.getX() +  ", " + camelLoc.getY() + " ]");

        //Try 1. update elicits Exception
        try
        {
            locations.put("LeftCamel"new Point(00));
        }
        catch (java.lang.UnsupportedOperationException e)
        {
            System.out.println("Try 1 - Could not update the map!");
        }

        //Try 2. Now let's try changing the contents of the object from the unmodifiable map!
        camelLoc.setLocation(00);

        //Now see whether we were able to update the actual map
        Point newCamelLoc = pieceLocations.getStartingLocations().get("LeftCamel");
        System.out.println("Try 2 - Map updated! The LeftCamel's start is now at [ " + newCamelLoc.getX() +  ", " + newCamelLoc.getY() + " ]");
    }
}

class Point
{
    public float x;
    public float y;
    public Point(float x, float y)
    {
        setLocation(x, y);
    }
    public void setLocation(float x, float y)
    {
        this.x = x;
        this.y = y;
    }

    public float getX()
    {
        return x;
    }

    public float getY()
    {
        return y;
    }
}

从这个代码可以看出:Collections.unmodifiableMap不是真正的不可以改变,其原理可以从Collections.unmodifiableMap的源代码看出来:

源代码如下:


 Java Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public V put(K key, V value)
{
    throw new UnsupportedOperationException();
}
public V remove(Object key)
{
    throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends V> m)
{
    throw new UnsupportedOperationException();
}
public void clear()
{
    throw new UnsupportedOperationException();
}

结论:Collections.unmodifiableMap是可以对map的值进行修改的,不可以采用put方式进行, 正确的姿势是对Value 采用Set方法进行修改

但是真实的使用场景Collections.unmodifiableMap是不需要对值进行修改的



@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
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>KuCun2</groupId> <artifactId>KuCun2</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>KuCun2</name> <description/> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> <!-- 请根据需要选择版本 --> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <webVersion>4.0</webVersion> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <protobuf.version>3.21.12</protobuf.version> </properties> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>javax.servlet.jsp.jstl</artifactId> <version>1.2.4</version> </dependency> <!-- Spring Boot Starter Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- MySQL Connector --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> <exclusions> <exclusion> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> </exclusion> </exclusions> </dependency> <!-- Optional: Lombok for reducing boilerplate code --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> </dependency> <!-- Jackson Databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- Jackson Core --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <!-- Jackson Annotations --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <exclusions> <exclusion> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>${protobuf.version}</version> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.30</version> <!-- 统一版本号 --> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArgs> <arg>-parameters</arg> </compilerArgs> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.6</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> </project> public AuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() { return (request, response, authentication) -> { // 制创建服务端会话 request.getSession(true); // 构建安全响应数据 Map<String, Object> responseData = new HashMap<>(); responseData.put("sessionId", request.getSession().getId()); responseData.put("userInfo", Collections.unmodifiableMap(new HashMap<>() {{ put( "displayName", ((CustomUserDetails)authentication.getPrincipal()).getName()); put("roleLevel", ((CustomUserDetails)authentication.getPrincipal()).getRole()) }})); // 统一返回JSON格式 response.setContentType(MediaType.APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getWriter(), responseData); }; }request报红
05-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值