利用 Map 集合的 containsKey 方法,实现对象数组的去重以及重复对象的字段值累加

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\"
   }
]

好了,思路清晰了,就去完成它吧。

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 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 一份原数组,遍历此数组,将相同姓氏的钱数累加,累加后得出的新数组再根据姓氏替换去重数组中的数据。这样就完成了数组去重并且相同字段值累加的工作。
看起来有点乱,一会一个去重数组,一会一个累加数组的。所以我画了一个图,看图说话:

利用 Map 集合的 containsKey 方法,实现对象数组的去重以及重复对象的字段值累加_第1张图片
弯路思路

虽然步骤多了点,但如果能得出正确结果也没毛病。(聪明的你一定猜到最后这种做法没走通,要不怎么叫弯路呢。)
数组去重难度不大,利用 Set 集合的不重复性,即可完成数组的去重工作:

Set lPersonSet = new HashSet();
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 mPersonsRecombine = new ArrayList();
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 lPersonMap = new HashMap();
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 数组。
然后,验证一下:

利用 Map 集合的 containsKey 方法,实现对象数组的去重以及重复对象的字段值累加_第2张图片
重组数据

结果证明,是正确的。
然后此刻我幡然醒悟,其实用 ArrayList 的 contains 方法也是可以的,因为我都已经重写了 Person 类的 equals() 与 hashCode() 方法,在 equals() 中我只要对姓名进行判断就好了。这样和 Map 的 containsKey 方法所完成的结果是一致的。当然 Map 的 containsKey 方法比 ArrayList 的 contains 效率提升的不是一星半点。

4. 总结

所幸,最终正确的答案被我找到了,也接触并学会了一个新知识 —— containsKey 方法。但是也要认清自己的不足,
一是受错误思路所困,现在回头看那个弯路思路是有多复杂和繁乱。另外,既已知道 ArrayList 的 contains 方法的含义和重写了 Person 类的 equals() 与 hashCode() 方法,也不知道将二者结合,去变通一下思路。
二是知识点掌握不全,如果一早知道 Map 集合的 containsKey 方法,也就不必费这些口舌与精力了。

还得练呐!~~~


利用 Map 集合的 containsKey 方法,实现对象数组的去重以及重复对象的字段值累加_第3张图片

你可能感兴趣的:(利用 Map 集合的 containsKey 方法,实现对象数组的去重以及重复对象的字段值累加)