利用 Map 集合的 containsKey 方法,实现对象数组的去重以及重复对象的字段值累加
2017-06-17 16:19
495 查看
1. 前言
公司的项目又加了一个新需求,打印发票增加详细收费方式以及每种收费收费金额。一开始没把它当回事,想着服务端返回的支付信息里包含着各种支付记录,在打印模块里将接收到的支付信息 List 遍历一下,然后打印出来就好了。后来做的时候发现,是我想得简单了。
因为服务端返回的支付信息是按照每笔交易记录返回的,即如果支付总额为20元,如果使用者支付了两次10元完成的支付,那么服务端存储的这笔交易的支付信息就为两次10元的支付记录。具体返回的 json 串示例如下:
"***INFOS\": [ { \"***NAME\": \"现金收费\", \"***ACCOUNT\": \"7.0\", \"***NUM\": \"1000000576\" }, { \"***NAME\": \"现金收费\", \"***ACCOUNT\": \"3.0\", \"***NUM\": \"1000000576\" }, { \"***NAME\": \"现金收费\", \"***ACCOUNT\": \"5.0\", \"***NUM\": \"1000000576\" }, { \"***NAME\": \"微信收费\", \"***ACCOUNT\": \"15.0\", \"***NUM\": \"1000000576\" }, { \"***NAME\": \"微信收费\", \"***ACCOUNT\": \"8.0\", \"***NUM\": \"1000000576\" } ]
可以看到,此次交易总额为38元,然后使用者分了5次完成的支付。如果我什么也不处理就开始遍历打印的话,打出的发票信息上显示的详细支付信息就是3条现金支付记录,和2条微信支付记录,只是钱数不同而已。这样是肯定不行的,所以这里要处理一下数据。
2. 思路
思路其实很简单,就是把 List 中的相同支付方式去重,然后将每笔支付金额相加,得出的总额算作是这种支付方式的支付钱数。即前文中那笔示例交易,处理后应该为:"***INFOS\": [ { \"***NAME\": \"现金收费\", \"***ACCOUNT\": \"15.0\", \"***NUM\": \"1000000576\" }, { \"***NAME\": \"微信收费\", \"***ACCOUNT\": \"23.0\", \"***NUM\": \"1000000576\" } ]
好了,思路清晰了
4000
,就去完成它吧。
3. 实现
实现的部分在新建的示例工程中完成。创建一个实体类对象:
public class Person { private String mName; private String mMoney; public Person(String pName, String pMoney) { mName = pName; mMoney = pMoney; } public String getName() { return mName; } public void setName(String pName) { mName = pName; } public String getMoney() { return mMoney; } public void setMoney(String pMoney) { mMoney = pMoney; } @Override public String toString() { return "Person{" + "mName='" + mName + '\'' + ", mMoney='" + mMoney + '\'' + '}'; } }
然后在代码中给实体类赋值,并新建一个该实体类的对象数组,将赋值后的实体类 add 到数组中:
private ArrayList<Person> mPersons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Person lPerson1 = new Person("王", "100"); Person lPerson2 = new Person("张", "200"); Person lPerson3 = new Person("李", "300"); Person lPerson4 = new Person("张", "400"); Person lPerson5 = new Person("王", "500"); Person lPerson6 = new Person("王", "800"); mPersons = new ArrayList<>(); mPersons.add(lPerson1); mPersons.add(lPerson2); mPersons.add(lPerson3); mPersons.add(lPerson4); mPersons.add(lPerson5); mPersons.add(lPerson6); }
到这里,初始化工作已经完成。相信你也已经看明白了,此时数组中存在6条数据,3条姓王的,2条姓张的,1条姓李的,并且每个人拥有的钱数都不一样。我们要做的,就是将此数组经过一系列处理后,数组内只让它留存3条数据,分别为1条姓王的,钱数1400;一条姓张的,钱数600,;一条姓李的,钱数300。
看起来很简单,但是我在实践的过程中还是走了一段弯路的。
3.1 弯路
一开始我是想这样处理的,先把数组中相同姓氏的去重,然后 copy 一份原数组,遍历此数组,将相同姓氏的钱数累加,累加后得出的新数组再根据姓氏替换去重数组中的数据。这样就完成了数组去重并且相同字段值累加的工作。看起来有点乱,一会一个去重数组,一会一个累加数组的。所以我画了一个图,看图说话:
虽然步骤多了点,但如果能得出正确结果也没毛病。(聪明的你一定猜到最后这种做法没走通,要不怎么叫弯路呢。)
数组去重难度不大,利用 Set 集合的不重复性,即可完成数组的去重工作:
Set<Person> lPersonSet = new HashSet<Person>(); lPersonSet.addAll(mPersons); mPersons.clear(); mPersons.addAll(lPersonSet);
这里要注意,如果只这么写,去重肯定是失败的。因为本身每个人的钱数就不同,所以它们就不是相同的对象。即便是姓名和钱数都相同,那每个对象在系统中的内存地址都不同,所以也不是相同对象,这样写一样会失败。
所以,这里要重写一下 Person 类的 equals() 与 hashCode() 方法。在 equals() 方法中,让它只比较姓名是否相同就可以了。
@Override public boolean equals(Object obj) { if (obj == null) return false; if (this == obj) return true; if (obj instanceof Person) { Person lPerson = (Person) obj; if (lPerson.mName.equals(this.mName)) { return true; } } return false; } @Override public int hashCode() { return mName.hashCode(); }
这样,去重工作就成功了。
问题就出在钱数累加上面了。我是这样写的:
ArrayList<Person> mPersonsRecombine = new ArrayList<Person>(); for (int i = 0; i < mPersons.size(); i++) { for (int j = 0; j < mPersons.size() - i; j++) { if(mPersons.get(i).getName().equals(mPersons.get(j).getName()) && i != j) { Person lPerson = new Person(mPersons.get(i).getName(),String.valueOf(Integer.parseInt( mPersons.get(i).getMoney()) + Integer.parseInt(mPersons.get(j).getMoney()))); mPersonsRecombine.add(lPerson); } } }
这段代码可以说是漏洞百出,首先,虽然通过 i != j 排除了相同 item 的累加情况,但是不同 item 相等的情况会出现两次。其次,超过两个相等姓氏的对象(比如数组中有3个姓王的),钱数会加不全。
之后思路就断在了这里,并且还围绕这这个问题做了很多修补工作,都是不行。我就准备换方法去实现了,总卡在这里也不行。
这里要说一下,也可能按照这种思路会有正确的打开方式,但是我实在是没有找到解。如果有解决出来的同学,一定要和我联系,一同讨论下。
3.2 解决
树挪死,人挪活。在经历前文所叙的弯路后,我就不打算在 ArrayList 中进行数据操作了,我打算把数据放在 Map 集合中试一下。然后我就去翻了下 Map 的 API,发现了这样一个方法—— containsKey。该方法判断 Map 集合对象中是否包含指定的键名。如果 Map 集合中包含指定的键名,则返回 true,否则返回 false。
看起来和 ArrayList 的 contains 方法差不多,但是这个方法可以直接对字段名进行判断。看起来可以搞。
Map<String, Person> lPersonMap = new HashMap<String, Person>(); for (Person lPerson : mPersons) { String personName = lPerson.getName(); if (lPersonMap.containsKey(personName)) { double personMoney = Double.valueOf(lPersonMap.get(personName).getMoney()); personMoney += Double.valueOf(lPerson.getMoney()); lPersonMap.get(personName).setMoney(Double.toString(personMoney)); } else { lPersonMap.put(personName, lPerson); } } mPersons.clear(); mPersons.addAll(lPersonMap.values());
逻辑十分简单,先建立了一个 Map 集合,然后遍历数组,获取数组内每个字段的名称,接着使用了 Map 的 containsKey 方法,判断 Map 集合中是否已经存在当前名称的数据。如果存在,就将钱数累加,如不存在,就将此数据存入 Map 集合中。最后将 Map 集合再转换成 List 数组。
然后,验证一下:
结果证明,是正确的。
然后此刻我幡然醒悟,其实用 ArrayList 的 contains 方法也是可以的,因为我都已经重写了 Person 类的 equals() 与 hashCode() 方法,在 equals() 中我只要对姓名进行判断就好了。这样和 Map 的 containsKey 方法所完成的结果是一致的。当然 Map 的 containsKey 方法比 ArrayList 的 contains 效率提升的不是一星半点。
4. 总结
所幸,最终正确的答案被我找到了,也接触并学会了一个新知识 —— containsKey 方法。但是也要认清自己的不足,一是受错误思路所困,现在回头看那个弯路思路是有多复杂和繁乱。另外,既已知道 ArrayList 的 contains 方法的含义和重写了 Person 类的 equals() 与 hashCode() 方法,也不知道将二者结合,去变通一下思路。
二是知识点掌握不全,如果一早知道 Map 集合的 containsKey 方法,也就不必费这些口舌与精力了。
还得练呐!~~~
相关文章推荐
- Map.containsKey方法——判断Map集合对象中是否包含指定的键名
- 集合中的contains方法怎么去实现,以及怎么重写对象的equals方法?
- Map.containsKey方法——判断Map集合对象中是否包含指定的键名
- 在主方法中创建map集合中存储3个学生对象.key使用字符串表示编号(不能重复)value是学生对象。然后调用以下两个方法;
- 利用Array Prototype的方法来实现对dom集合的筛选、indexOf、map等功能
- Map.containsKey方法——判断Map集合对象中是否包含指定的键名
- 【js jQuery】map集合 循环迭代取值---以及 map、json对象、list、array循环迭代的方法和区别
- Java集合中那些类是线程安全的 以及 Map线程安全几种实现方法
- 利用hashtable模拟实现权限验证(同map思想-->只能有一个用户名,可重复密码)以及增删查改操作
- 向ArrayList集合中存入对象,并以让集合用自己设定的方式去除重复元素!(覆写equals方法)
- Java:对集合中的对象进行排序需要实现Interface Comparable接口并实现int compareTo(T o)方法
- 利用HashSet实现数组重复对象属性简单合并
- C#反射新建类实例和调用类方法及属性帮助类 以及获取或设置对象属性中字段值帮助类
- 利用Lambda表达式、扩展方法以及泛型来为对象添加方法
- Java集合对象的排序使用Collection.sort方法,先实现Comparable接口
- 利用集合的静态方法Collections.shuffle() 乱序集合中的元素,实现自动理牌
- 利用"委托"实现类的对象实例按"多字段嵌套"排序
- 利用"委托"实现对象实例按"多字段嵌套"排序
- 向HashSet集合存入对象,去除重复元素(覆写equals和hashCode方法)
- .NET/C#中对对象集合进行查询的方法 以及相关的 Predicate<T> 及 Action<T> 的用法