jsonobject判断key是否存在某个key(java的foreach用法)

jsonobject判断key是否存在某个key(java的foreach用法)

cnblogs.com/yougewe/p/14258485.html

目录保持json有序的思路保持json有序的应用场景举例fastjson维护json的有序性的实现hashmap数据结构的顺序迭代原理

说到json,相信没有人会陌生,我们天天都在用。那么,我们来讨论个问题,json有序吗?是谁来决定的呢?如何保持?

说到底,json是框架还是啥?实际上它只是一个数据格式,一个规范标准,它永远不会限制实现方的任何操作,即不会自行去保证什么顺序性之类的。json的格式仅由写入数据的一方决定其长像如何。而数据读取一方,则按照json的协议标准进行解析,即可理解原数据的含义。json拥有较为丰富的数据格式,所以对当前应用还是比较友好的。

那么,我们如何处理json的顺序性呢?

1. 保持json有序的思路

首先,我们要澄清有序性的概念:从某种程度上,我们可以把json看作是一个个的kv组成的数据,从这个层面上来讲,我们可以把有序性定义为json的key保持有序,先假设为字典序吧,那么就说这个json数据是有序的。

其次,因为json的数据支持嵌套,所以,我们应该需要保持每一层的数据都有序,才是完整有序的。

ok, 理解完有序的概念,下面我们来看看如何实现有序?

json本身是不可能保持有序了,所以,当我们自行写入json数据时,只需要按照 abcde… 这种key顺序写入数据,那么得到的最终json就是有序的。

但我们一般都是使用对象进行程序变换的,所以,就应该要从对象中取出有序的key, 然后序列化为json.

这里保持有序,至少有两个层面的有序:1. kv形式的key的有序; 2. 列表形式的数据有序; 还有其他可能非常复杂的有序性需求,比如按照某字段有序,倒序。。。

所以,想保持json有序很简单,保证有序写入就可以了。(貌似等于没有说哦)

2. 保持json有序的应用场景举例

为什么要保持json有序呢?json相当于kv数据,一般情况下我们是不需要保证有序的,但有些特殊情况下也许有用。比如我有两份json数据,我想比较它们是否是相等的时候!

比如第一份数据是 {“a”:1, “b”:2}, 第二份数据是 {“b”:2, “a”:1}, 那么你说这两份数据是否是相等的呢?相等或不相等依据是啥?

如果对于固定数据结构的json, 那么也许我们可以直接取出每个key的值,然后进行比较,全部相等则相等成立,否则不相等。

但对于json本身就是各种不确定的数据组成,如果我们限制死必须取某些key, 那么这个通用性就很差了。所以,我们要想比较两个json是否相等,还应该要有另外的依据。

另外,当我们将有序json写入文件之后,当key的数据非常多时,有序实际上可以辅助我们快速找到对应的key所在的位置。这是有序性带来的好处,快速查找!

比如下面的例子,对比两个结果集是否相等,你觉得结果当如何呢?

@TestpublicvoidtestJsonObjectOrder(){Stringres1,res2;List<Map<String,Object>>nList;Map<String,Object>data=newHashMap<>();data.put(“d”,”cd”);data.put(“a”,1);data.put(“b”,0.45);data.put(“total”,333);List<Map<String,Object>>list=newArrayList<>();Map<String,Object>item1=newHashMap<>();item1.put(“aa”,1);item1.put(“ee”,5);item1.put(“bb”,6);item1.put(“nn”,null);list.add(item1);Map<String,Object>item2=newHashMap<>();item2.put(“xxx”,”000″);item2.put(“q”,2);item2.put(“a”,”aa”);list.add(item2);data.put(“sub”,list);Map<String,Object>nData=newHashMap<>(data);nData.put(“c”,null);nData.put(“abc”,null);res1=JSONObject.toJSONString(data);res2=JSONObject.parseObject(JSONObject.toJSONString(nData)).toJSONString();Assert.assertEquals(“序列化结果不相等default”,res1,res2);res2=JSONObject.toJSONString(JSONObject.parseObject(JSONObject.toJSONString(nData,SerializerFeature.SortField)),SerializerFeature.SortField);Assert.assertEquals(“序列化结果不相等sort”,res1,res2);nList=newArrayList<>();nList.add(item2);nList.add(item1);nData.put(“sub”,nList);res2=JSONObject.parseObject(JSONObject.toJSONString(nData)).toJSONString();Assert.assertEquals(“序列化结果不相等array”,res1,res2);}

以上是fastjson库进行json序列化的处理方式,json的数据结构大部分使用可以用map进行等价,除了纯数组的结构以外。以上测试中,除了最后一个array的位置调换,导致的结果不一样之外,总体还是相等的。纠其原因,是因为原始数据结构是一致的,而fastjson从一定程度上维持了这个有序性。

3. fastjson维护json的有序性的实现

很显然,让我们自行写json的工具类,还是有一定的难度的,至少要想高效完整地写json是困难的。所以,一般我们都是借助一些现有的开源类库。

上一节中说到,fastjson维护了json一定的顺序性,但是并非完整维护了顺序性,它的顺序性要体现在,相同的数据结构序列化的json,总能得到相同的反向的相同数据结构的数据。比如,ArrayList 的顺序性被维护,map的顺序性被维护。

但是很明显,这些顺序性是根据数据结构的特性而定的,而非所谓的字典序,那么,如果我们想维护一个保持字典序的json如何处理呢?看看下面的实现:

publicclassJsonObjectTest{@TestpublicvoidtestJsonObjectOrder(){Stringres1,res2;List<Map<String,Object>>nList;Map<String,Object>data=newHashMap<>();data.put(“d”,”cd”);data.put(“a”,1);data.put(“b”,0.45);data.put(“total”,333);List<Map<String,Object>>list=newArrayList<>();Map<String,Object>item1=newHashMap<>();item1.put(“aa”,1);item1.put(“ee”,5);item1.put(“bb”,6);item1.put(“nn”,null);list.add(item1);Map<String,Object>item2=newHashMap<>();item2.put(“xxx”,”000″);item2.put(“q”,2);item2.put(“a”,”aa”);list.add(item2);data.put(“sub”,list);Map<String,Object>nData=newHashMap<>(data);nData.put(“c”,null);nData.put(“abc”,null);res1=JSONObject.toJSONString(data);res2=JSONObject.parseObject(JSONObject.toJSONString(nData)).toJSONString();Assert.assertEquals(“序列化结果不相等default”,res1,res2);res2=JSONObject.toJSONString(JSONObject.parseObject(JSONObject.toJSONString(nData,SerializerFeature.SortField)),SerializerFeature.SortField);Assert.assertEquals(“序列化结果不相等sort”,res1,res2);res2=JSONObject.toJSONString(JSONObject.parseObject(JSONObject.toJSONString(nData,SerializerFeature.WriteMapNullValue)),SerializerFeature.WriteMapNullValue);Assert.assertEquals(“序列化结果不相等null”,res1,res2);nList=newArrayList<>();nList.add(item2);nList.add(item1);nData.put(“sub”,nList);res2=JSONObject.parseObject(JSONObject.toJSONString(nData)).toJSONString();Assert.assertEquals(“序列化结果不相等array”,res1,res2);nList=newArrayList<>();nList.add(item2);nList.add(item1);nData.put(“sub”,nList);res1=transformDataToJSONAsOrderWay(data);res2=transformDataToJSONAsOrderWay(nData);Assert.assertEquals(“序列化结果不相等array-s”,res1,res2);}/***将原始数据转换为有序的集合*/privateStringtransformDataToJSONAsOrderWay(Map<String,Object>data){TreeMap<String,Object>transformedData=newTreeMap<>();for(Map.Entry<String,Object>entry:data.entrySet()){if(entry.getValue()==null){continue;}if(entry.getValue()instanceofList){TreeMap<String,Integer>tmpMap=newTreeMap<>();Listvalue=(List)entry.getValue();for(inti=0;i<(value).size();i ){Objectit1=value.get(i);//假设只支持二维数组嵌套tmpMap.put(transformDataToJSONAsOrderWay((Map<String,Object>)it1),i);}List<Object>orderedList=newArrayList<>(tmpMap.size());for(IntegerlistNo:tmpMap.values()){orderedList.add(value.get(listNo));}transformedData.put(entry.getKey(),orderedList);continue;}transformedData.put(entry.getKey(),entry.getValue());}returnJSONObject.toJSONString(transformedData);}}

以上就是完整的基于fastjson实现的json字典序维持的实现了,其实就是 transformDataToJSONAsOrderWay() 方法,其原理也简单,因fastjson的有序性,依赖于输入的数据结构,那么只要维护好输入结构的字典序就好了。

TreeMap 是以字典序排序key的一种数据结构,符合这需求,另外,将list这种数据结构,转化为kv这种数据结构,将整个item作为key排序后,再将其放入对应位置,从而保证了整体的顺序性。但这种list的顺序性,不一定是大家所理解的字典序,但一定可以保证得到相同的顺序。

另外,fastjson中还考虑了对于null值的处理,比如json中有null值的数据与没有null值的数据,你说是相等呢还是不相等呢?

4. hashmap数据结构的顺序迭代原理

map是一种kv型的数据结构存储,一般可以认为其是无序的。但我们可以额外的维护一些属性,以保证它能够以某种顺序输出数据,顺序性主要体现在进行迭代时,如使用 keyset(), values(), entrySet() 等方法。

针对额外维护顺序性的数据结构而言,其迭代自然是基于其额外字段。但针对无序的hashmap这种数据结构而言,我们知道其底层数据是根据hash值乱序存储的。简单来说就是根据一个hash值,然后求余定位到一个数组下标中。

即对hashmap所分配的数组对象的下标,有可能有值,有可能没有值,那么在做迭代的时候如何做呢?多次做迭代的顺序一致吗?一个最简单的思路自然是依次遍历数据的每个元素,直到数据的最大值。这样,肯定是可以保证多次遍历的顺序性的。那么,hashmap是否是这样实现的呢?

//java.util.HashMap#forEach@OverridepublicvoidforEach(BiConsumer<?superK,?superV>action){Node<K,V>[]tab;if(action==null)thrownewNullPointerException();if(size>0&&(tab=table)!=null){intmc=modCount;//1.迭代所有数组元素//2.迭代hash冲突时的链表for(inti=0;i<tab.length; i){for(Node<K,V>e=tab[i];e!=null;e=e.next)action.accept(e.key,e.value);}if(modCount!=mc)thrownewConcurrentModificationException();}}//java.util.HashMap.EntrySet#forEachpublicfinalvoidforEach(Consumer<?superMap.Entry<K,V>>action){Node<K,V>[]tab;if(action==null)thrownewNullPointerException();if(size>0&&(tab=table)!=null){intmc=modCount;for(inti=0;i<tab.length; i){for(Node<K,V>e=tab[i];e!=null;e=e.next)action.accept(e);}if(modCount!=mc)thrownewConcurrentModificationException();}}//keySet(),换成了取key的处理//java.util.HashMap.KeySet#forEachpublicfinalvoidforEach(Consumer<?superK>action){Node<K,V>[]tab;if(action==null)thrownewNullPointerException();if(size>0&&(tab=table)!=null){intmc=modCount;for(inti=0;i<tab.length; i){for(Node<K,V>e=tab[i];e!=null;e=e.next)action.accept(e.key);}if(modCount!=mc)thrownewConcurrentModificationException();}}//values()换成了取value的处理//java.util.HashMap.Values#forEachpublicfinalvoidforEach(Consumer<?superV>action){Node<K,V>[]tab;if(action==null)thrownewNullPointerException();if(size>0&&(tab=table)!=null){intmc=modCount;for(inti=0;i<tab.length; i){for(Node<K,V>e=tab[i];e!=null;e=e.next)action.accept(e.value);}if(modCount!=mc)thrownewConcurrentModificationException();}}

TreeMap基于key做排序处理,最符合有序性要求,其迭代实现如下:

//java.util.TreeMap#forEach@OverridepublicvoidforEach(BiConsumer<?superK,?superV>action){Objects.requireNonNull(action);intexpectedModCount=modCount;//从最小的key开始取,进行二叉树的中序遍历//因该二叉树在插入时维护了有序性,进行遍历时也就有了顺序了for(Entry<K,V>e=getFirstEntry();e!=null;e=successor(e)){action.accept(e.key,e.value);if(expectedModCount!=modCount){thrownewConcurrentModificationException();}}}/***ReturnsthesuccessorofthespecifiedEntry,ornullifnosuch.*/static<K,V>TreeMap.Entry<K,V>successor(Entry<K,V>t){if(t==null)returnnull;elseif(t.right!=null){Entry<K,V>p=t.right;while(p.left!=null)p=p.left;returnp;}else{Entry<K,V>p=t.parent;Entry<K,V>ch=t;while(p!=null&&ch==p.right){ch=p;p=p.parent;}returnp;}}

LinkedHashMap是按照插入的顺序排列的一种map, 它与ArrayList这种有序性有相似性,但相对难以理解一些。因为list这种数据结构,你说先插入哪个元素,后插入哪个元素,是显而易见的。

然而像map这种数据结构,你很想像它是先插入某元素,再插入另一个元素的,这是一种先入为主的概念导致的。但它并不影响我们理解map有序性的实现,LinkedHashMap的迭代实现如下:

//java.util.LinkedHashMap#forEachpublicvoidforEach(BiConsumer<?superK,?superV>action){if(action==null)thrownewNullPointerException();intmc=modCount;//仅通过维护插入时的链表,即可实现有序迭代for(LinkedHashMap.Entry<K,V>e=head;e!=null;e=e.after)action.accept(e.key,e.value);if(modCount!=mc)thrownewConcurrentModificationException();}

ok, 到此我们分析了多个类型的map的有序性的实现。从内部解释了为什么我们使用TreeMap数据结构时,就可以使json保持字典序了。

因为fastjson在写json数据时,针对map的写入,就是通过entrySet()迭代元素进行写入的了。

推荐好文

分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!

能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!

发表评论

登录后才能评论