Java中List和Map的特性对两组大批量数据进行匹配 (转)
在项目中遇到一个问题:要将通过http方式发送过来的大批量数据(这个数据保守估计每次请求在10万条左右),要和数据库中的另一批数据(数据库中的记录1万条左右)进行匹配(匹配:指两组数据中的某几个字段值相等),匹配上的数据保存在数据库中,匹配不上的直接扔掉。或者说:有一个List<String> strList,List<Person> personList,strNoList.size是1万,personList.size是10万, 然后要从personList中把person的id属性在strList中的person取出来,personList中的person的id可能会相同,两个记录的结构不同。
要实现这个功能,首先想到的就是使用for循环逐条进行比较,那么这样就有10W*1W,即10亿次循环。但是,系统对数据的实时性要求比较高,这样做显然性能上是有问题的。于是乎就要找另一种方式,减少循环次数来提高匹配的处理速度,由于之前也没做个这样的事情,于是就想各种办法,同时在OSC社区发帖求助
List可以放重复数据,而Map为不能放重复数据的key-value结构。那么就可以把接收到的id相同的person实体数据放入一个list中,然后用该id作为key,list做作为value放入map中。那么现在处理10w条数据则需要10W次for循环。然后查出数据库中的1W条记录,遍历map,使用map.get("key")取出相同id的list,然后将这些list的元素全部添加到一个resultList中,遍历这1W条记录需要1W次for循环。这样,就将一个10W*1W次的for循环减小到10W+1W次。下边是关于for循环次数的耗时测试,结果表明减少循环次数能大幅度提高处理速度
1 import java.util.ArrayList; 2 import java.util.HashMap; 3 import java.util.List; 4 import java.util.Map; 5 /** 6 * For循环测试 7 * @author 大漠 8 * 9 */ 10 public class ForTest { 11 public static void main(String[] args) { 12 ForTest test = new ForTest(); 13 System.out.println("============开始============="); 14 //一亿次for循环 15 test.test1Yi(); 16 //十一万次for循环 17 test.test11W(); 18 //嵌套for循环匹配:10W*1W次for循环 19 test.testForAndFor(); 20 //Map和List整理匹配:10W+1W次for循环 21 test.testMapAndList(); 22 System.out.println("============结束============="); 23 } 24 /** 25 * 一亿次for循环 26 */ 27 private void test1Yi(){ 28 long start = System.currentTimeMillis(); 29 for (Integer i = 0; i < 100000000;i++) { 30 System.out.println(i); 31 } 32 long end = System.currentTimeMillis(); 33 System.out.println("1亿次循环耗时:"+ (end - start) + "毫秒"); 34 System.out.println("----------------------------"); 35 } 36 37 /** 38 * 11万次for循环 39 */ 40 private void test11W(){ 41 long start = System.currentTimeMillis(); 42 for (Integer i = 0; i < 110000;i++) { 43 System.out.println(i); 44 } 45 long end = System.currentTimeMillis(); 46 System.out.println("11W次循环耗时:"+ (end - start) + "毫秒"); 47 System.out.println("----------------------------"); 48 } 49 50 /** 51 * 嵌套for循环进行比较匹配 52 */ 53 private void testForAndFor(){ 54 //构造一个10万个Person对象的list 55 List<Person> personList = new ArrayList<Person>(); 56 for (int i = 0; i < 100000; i++) { 57 int j =10000; 58 personList.add(new Person((i%j) +"", "张三"+i)); 59 } 60 //构造一个1万个String对象的list 61 List<String> strList = new ArrayList<String>(); 62 for (int i = 0; i < 10000; i++) { 63 strList.add(i +""); 64 } 65 66 //嵌套for循环:10W*1W 67 long start = System.currentTimeMillis(); 68 //保存匹配结果 69 List<Person> resultList = new ArrayList<Person>(); 70 //遍历10W person的List 71 for (Person person : personList) { 72 //遍历1W str的List 73 for (String str : strList) { 74 if(person.getId().equals(str)){ 75 resultList.add(person); 76 } 77 } 78 } 79 long end = System.currentTimeMillis(); 80 System.out.println("reslutList.size:"+ resultList.size()); 81 System.out.println("10W*1W次循环耗时:"+ (end - start) + "毫秒"); 82 System.out.println("----------------------------"); 83 } 84 85 /** 86 * 使用Map和List的特性进行匹配: 87 * Map为key-value结构,不能放重复数据 88 * List可以放重复数据 89 * 使用String型id做key,List<Person>做value 90 * 遍历List<String>, map.get(String)则取出id == str 的List 91 */ 92 private void testMapAndList(){ 93 //构造一个10万个Person对象的list 94 List<Person> personList = new ArrayList<Person>(); 95 96 for (int i = 0; i < 100000; i++) { 97 int j =10000; 98 personList.add(new Person((i%j) +"", "张三"+i)); 99 } 100 //构造一个1万个String对象的list 101 List<String> strList = new ArrayList<String>(); 102 for (int i = 0; i < 10000; i++) { 103 strList.add(i +""); 104 } 105 106 long start = System.currentTimeMillis(); 107 //利用Map和List的特性整理数据 108 Map<String, List<Person>> map = new HashMap<String, List<Person>>(); 109 //将10W条数据根据id放入map 110 for(int i=0;i<personList.size();i++){ 111 Person person = personList.get(i); 112 String id = person.getId(); 113 if(map.containsKey(id)){ 114 map.get(id).add(person); 115 }else{ 116 List<Person> pList = new ArrayList<Person>(); 117 pList.add(person); 118 //id为key,相同id的person的List为value 119 map.put(id,pList); 120 } 121 } 122 //保存匹配结果 123 List<Person> resultList = new ArrayList<Person>(); 124 //根据1W条str,map.get(str)取匹配上的数据 125 for (String str : strList) { 126 List<Person> pList = map.get(str); 127 if (pList != null) { 128 resultList.addAll(pList); 129 } 130 } 131 long end = System.currentTimeMillis(); 132 System.out.println("map.size:" +map.size()); 133 System.out.println("reslutList.size:"+ resultList.size()); 134 System.out.println("10W+1W次循环耗时:"+ (end - start) + "毫秒"); 135 System.out.println("----------------------------"); 136 } 137 } 138 /** 139 * Person实体类 140 */ 141 class Person{ 142 private String id; 143 private String name; 144 public Person() {} 145 146 public Person(String id, String name) { 147 this.id = id; 148 this.name = name; 149 } 150 @Override 151 public String toString() { 152 return this.id +"::>"+ this.name; 153 } 154 155 public String getId() { 156 return id; 157 } 158 public void setId(String id) { 159 this.id = id; 160 } 161 public String getName() { 162 return name; 163 } 164 public void setName(String name) { 165 this.name = name; 166 } 167 }
测试结果:
============开始=============
1亿次循环耗时:1262985毫秒
----------------------------
11W次循环耗时:1016毫秒
----------------------------
reslutList.size:100000
10W*1W次循环耗时:21219毫秒
----------------------------
map.size:10000
reslutList.size:100000
10W+1W次循环耗时:31毫秒
============结束=============
•1亿次system.out.println(i)的循环耗时1262985毫秒,即21分钟,那么10亿次210分钟,显然不可接受。当然这里设计I/O操作,比较耗时,实际应用中没有这么吓人。
•11万次system.out.println(i)循环耗时1016毫秒,即1秒种,很明显,减少循环次数能够提高处理速度。
•使用嵌套for循环完成10W*1W次循环耗时21219毫秒,使用第二种方法完成10W+1W次循环耗时31毫秒,处理速度提高了600多陪,达到了预想的目的。