您的位置:首页 > 编程语言 > Java开发

关于List.addAll(Collection<E>)方法遇到的问题

2017-07-13 11:40 746 查看
涉及的软件环境:SpringMVC,fastjson

有一个需求,需要将热词信息跟自定义的热词信息(另外一个实体的信息)合并到一个列表里,

获取hotKeyList的代码

List<HotKeys> hotKeyList = hotKeysDao.getAllHotKeys();


获取自定义热词的代码:

List<AdInfoExt> comprehensivePageAds = adInfoDao.getAdInfoListByRuleCode("comprehensive_search_page", "ad");
List<HotKeys> cpaKeys = new ArrayList<HotKeys>();
for(AdInfoExt ad : comprehensivePageAds){
HotKeys key = new HotKeys();
key.setHotKey(ad.getAdTittle());
key.setStats(1);
key.setUrl(ad.getUrl());
cpaKeys.add(key);
}


显然最终都是封装成
List<HotKeys>
了,然后将
hotKeyList
cpaKeys
合并到一起用了:

cpaKeys.addAll(hotKeyList);


或者:

for(HotKeys k : hotKeyList){
cpaKeys.add(k);
}


都产生了下面的问题:输出到客户端的时候,
cpaKeys
是正常的,

"cpaKey": [
{
"k": "杨振宁遗产分配",
"sta": 0,
"u": "https://wap.sogou.com/web/searchList.jsp?keyword=%E6%9D%A8%E6%8C%AF%E5%AE%81%E9%81%97%E4%BA%A7%E5%88%86%E9%85%8D&pid=sogou-mobp-eeea8c180c5dff16&v=5"
},
{
"k": "泉州6车追尾",
"sta": 0,
"u": "https://wap.sogou.com/web/searchList.jsp?keyword=%E6%B3%89%E5%B7%9E6%E8%BD%A6%E8%BF%BD%E5%B0%BE&pid=sogou-mobp-eeea8c180c5dff16&v=5"
},
{
"k": "翻车致19人遇难",
"sta": 1,
"u": "https://wap.sogou.com/web/searchList.jsp?keyword=%E7%BF%BB%E8%BD%A6%E8%87%B419%E4%BA%BA%E9%81%87%E9%9A%BE&pid=sogou-mobp-eeea8c180c5dff16&v=5"
}
]


而原来的
hotKeyList
的数据就变成了以下的模样(它正确的样子应该跟上面的
cpaKey
一样):

"hotKey": [
{
"$ref": "$.cpaKey[0]"
},
{
"$ref": "$.cpaKey[1]"
},
{
"$ref": "$.cpaKey[2]"
},
{
"$ref": "$.cpaKey[3]"
},
{
"$ref": "$.cpaKey[4]"
},
{
"$ref": "$.cpaKey[5]"
},
{
"$ref": "$.cpaKey[6]"
},
{
"$ref": "$.cpaKey[7]"
}
]


后来将两个list的合并方式改成对象新对象拷贝旧对象信息,将新对象存入列表的方式,就两个都正常了:

for(HotKeys key : hotKeyList){
HotKeys k = new HotKeys();
BeanUtils.copyProperties(key, k);
cpaKeys.add(k);
}


这是什么问题?对象引用的问题?

从上面的运行结果可以看出是
hotKeyList
里面的对象引用发生变化,转而指向
cpaKeys
中对应的数据,经过
addAll()
方法处理后
hotKeyList
只保存了每个对象的引用信息(或者说指针)。
hotKeyList
并没有另外生成一份数据,看看源码:

public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);  // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}


其中方法
System.arraycopy(a, 0, elementData, size, numNew);
的描述如下:

Copies an array from the specified source array, beginning at the specified position, to the specified position of the destination array. A subsequence of array components are copied from the source array referenced by src to the destination array referenced by dest. The number of components copied is equal to the length argument. The components at positions srcPos through srcPos+length-1 in the source array are copied into positions destPos through destPos+length-1, respectively, of the destination array.

If the src and dest arguments refer to the same array object, then the copying is performed as if the components at positions srcPos through srcPos+length-1 were first copied to a temporary array with length components and then the contents of the temporary array were copied into positions destPos through destPos+length-1 of the destination array.

第一段话说到”数组组件的子序列从src引用的源数组复制到dest引用的目标数组。”注意“引用的源数组”、“引用的目标数组”的意思。

而通过SpringMVC的
@ResponseBody
返回JSON的时候,FastJSON并没有将引用转成数据,为了避免日后还会产生这样的问题,最终的解决方案是将FastJSON换为Jackson。配置文件修改如下:

pom.xml 新增jackson的依赖:

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.2</version>
</dependency>


spring-mvc.xml修改如下:

<!--避免IE执行AJAX时,返回JSON出现下载文件.原来的(fastjson)class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"-->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
</list>
</property>
<!-- 使用jackson不需要这个
<property name="features">
<list>
<!// <value>WriteMapNullValue</value> //>
<value>QuoteFieldNames</value>
</list>
</property> -->
</bean>

<!-- 修改新增 -->
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 修改新增 -->
<bean id="formHttpMessageConverter" class="org.springframework.http.converter.FormHttpMessageConverter" />

<!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="formHttpMessageConverter" />     <!-- 修改新增 -->
<ref bean="stringHttpMessageConverter" />   <!-- 修改新增 -->
<ref bean="mappingJacksonHttpMessageConverter" />   <!-- JSON转换器 -->
</list>
</property>
</bean>


还是使用最简单
addAll()
方法,结果正常。

这次疏忽导致的影响非常大,虽然问题藏得有点深,但关键还是没有去验证对原来数据影响导致,以此作为一次深刻经验教训。

另外,经过集思广益,总结出以上出现$ref的问题的根本是因为对象中存在重复的对象导致,如果仍然使用fastjson方法的话可以这么处理:

FastJson提供了
SerializerFeature.DisableCircularReferenceDetect
这个序列化选项,用来关闭引用检测。关闭引用检测后,重复引用对象时就不会被$ref代替,但是在循环引用时也会导致StackOverflowError异常。

用法:

JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);


得到的是object的json字符串。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java-web 问题 List addAll
相关文章推荐