Jackson如何缓存key(在什么时机使用了String.intern()方法)

Jackson反序列化有两个配置特性INTERN_FIELD_NAMES和CANONICALIZE_FIELD_NAMES,用于决定是否规范化JSON键并调用intern方法。当CANONICALIZE_FIELD_NAMES生效时,INTERN_FIELD_NAMES才起作用。在解析JSON时,Jackson通过ConcurrentHashMap避免重复调用intern,提高性能。

两个特性的作用

Jackson在反序列化是有两个可配置的特性:INTERN_FIELD_NAMES和CANONICALIZE_FIELD_NAMES。

 /**
         * Feature that determines whether JSON object field names are
         * to be canonicalized using {@link String#intern} or not:
         * if enabled, all field names will be intern()ed (and caller
         * can count on this being true for all such names); if disabled,
         * no intern()ing is done. There may still be basic
         * canonicalization (that is, same String will be used to represent
         * all identical object property names for a single document).
         *<p>
         * Note: this setting only has effect if
         * {@link #CANONICALIZE_FIELD_NAMES} is true -- otherwise no
         * canonicalization of any sort is done.
         *<p>
         * This setting is enabled by default.
         */
        INTERN_FIELD_NAMES(true),

        /**
         * Feature that determines whether JSON object field names are
         * to be canonicalized (details of how canonicalization is done
         * then further specified by
         * {@link #INTERN_FIELD_NAMES}).
         *<p>
         * This setting is enabled by default.
         */
        CANONICALIZE_FIELD_NAMES(true)

根据注释可以看出来,这两个特性的作用是决定是否要规范化json串的key(即调用key的intern方法),并且只有在CANONICALIZE_FIELD_NAMES生效时,INTERN_FIELD_NAMES才会生效。


那么,这个两个特性是如何起作用的呢?

在获取json串的下一个token时,即ReaderBasedJsonParser的nextToken()方法中,解析了key的名称,代码如下:

boolean inObject = _parsingContext.inObject();
        if (inObject) {
           // First, field name itself:
            String name = _parseName(i);//解析key的名称
            _parsingContext.setCurrentName(name);
            _currToken = JsonToken.FIELD_NAME;
            i = _skipWS();
            if (i != INT_COLON) {
                _reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
            }
            i = _skipWS();
        }

在跟到_parseName(int i)中,可以发现这个方法调用了_parseName2(int startPtr, int hash, int endChar),方法_parseName2方法返回的是
_symbols.findSymbol(buf, start, len, hash),看一下findSymbol的实现会先后看到下面两个实现逻辑
逻辑1:
if (!_canonicalize) { // [JACKSON-259]
    return new String(buffer, start, len);
}
逻辑2:
 String newSymbol = new String(buffer, start, len);
        if (_intern) {
            newSymbol = InternCache.instance.intern(newSymbol);
        }

逻辑1中,如果没有配置CANONICALIZE_FIELD_NAMES,直接返回了key的名字,逻辑2中,如果配置了INTERN_FIELD_NAMES,调用intern方法。

_symbols及两个字段_canonicalize和_intern是怎么初始化的

在ObjectMapper的readValue(String content, Class<T> valueType)方法中,先初始化了jsonParser(在JsonFactory的createParser方法中),
protected JsonParser _createParser(Reader r, IOContext ctxt)
        throws IOException, JsonParseException
    {
        return new ReaderBasedJsonParser(ctxt, _parserFeatures, r, _objectCodec,
                _rootCharSymbols.makeChild(isEnabled(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES),
                        isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)));
    }

构造ReaderBasedJsonParser的第三个参数是,完成了_symbols及两个字段的初始化。

InternCache是什么

它实现了ConcurrentHashMap,为的是防止多次调用同一个字符串的intern方法。


### 使用场景 在Java中,`String.intern()`方法主要用于以下几种场景: 1. **内存优化**:当应用程序中存在大量相同内容的字符串时,使用`intern()`可以减少内存开销,避免创建多个相同内容的字符串对象[^3]。 2. **性能优化**:通过`intern()`方法实现字符串的快速比较,因为可以直接比较字符串引用,而不需要逐字符比较内容[^2]。 ### 作用 `String.intern()`方法的作用是将字符串对象添加到字符串常量池中,或者返回常量池中已经存在的相同字符串的引用。这意味着如果多次调用`intern()`方法处理相同内容的字符串对象,最终将获得同一个字符串实例的引用,从而提高内存效率和比较速度[^1]。 ### 示例代码 下面是一个简单的示例,展示了`String.intern()`方法的效果: ```java public class StringInternExample { public static void main(String[] args) { String str1 = new String("hello"); String str2 = new String("hello"); // str1 和 str2 是不同的对象 System.out.println(str1 == str2); // 输出 false // 将 str1 和 str2 intern 到常量池中 String str3 = str1.intern(); String str4 = str2.intern(); // str3 和 str4 引用的是同一个常量池中的字符串 System.out.println(str3 == str4); // 输出 true } } ``` 在这个例子中,尽管`str1`和`str2`是两个不同的对象,但它们的内容相同。调用`intern()`方法后,`str3`和`str4`指向了同一个字符串实例,这表明它们现在引用的是字符串常量池中的同一个对象[^3]。 ### 小结 通过合理利用`String.intern()`方法,可以在特定场景下显著减少内存占用,并提升字符串比较的性能。这对于处理大量重复字符串的应用程序尤其有用,例如解析大量文本数据时,可以有效降低内存消耗并提高程序运行效率[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值