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循環次數的耗時測試,結果表明減少循環次數能大幅度提高處理速度

 

所以我們一般寫代碼,雙循環匹配是N*M,數據量不大可以,數據量大了那就。。。

 

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * For循環測試
 * @author 47Gamer
 *
 */
public class ForTest {
 public static void main(String[] args) {
  ForTest test = new ForTest();
  System.out.println("============開始=============");
   //一億次for循環
   test.test1Yi();
   //十一萬次for循環
   test.test11W();
   //嵌套for循環匹配:10W*1W次for循環
   test.testForAndFor();
   //Map和List整理匹配:10W+1W次for循環
   test.testMapAndList();
  System.out.println("============結束=============");
 }
 /**
  * 一億次for循環
  */
 private void test1Yi(){
  long start = System.currentTimeMillis();
  for (Integer i = 0; i < 100000000;i++) {
   System.out.println(i);
  }
  long end = System.currentTimeMillis();
  System.out.println("1億次循環耗時:"+ (end - start) + "毫秒");
  System.out.println("----------------------------");
 }

 /**
  * 11萬次for循環
  */
 private void test11W(){
  long start = System.currentTimeMillis();
  for (Integer i = 0; i < 110000;i++) {
   System.out.println(i);
  }
  long end = System.currentTimeMillis();
  System.out.println("11W次循環耗時:"+ (end - start) + "毫秒");
  System.out.println("----------------------------");
 }

 /**
  * 嵌套for循環進行比較匹配
  */
 private void  testForAndFor(){
  //構造一個10萬個Person對象的list
  List<Person> personList = new ArrayList<Person>();
  for (int i = 0; i < 100000; i++) {
   int j =10000;
   personList.add(new Person((i%j) +"", "張三"+i));
  }
  //構造一個1萬個String對象的list
  List<String> strList = new ArrayList<String>();
  for (int i = 0; i < 10000; i++) {
   strList.add(i +"");
  }

  //嵌套for循環:10W*1W
  long start = System.currentTimeMillis();
  //保存匹配結果
        List<Person> resultList = new ArrayList<Person>();
        //遍歷10W person的List
        for (Person person : personList) {
         //遍歷1W str的List
            for (String str : strList) {
                if(person.getId().equals(str)){
                    resultList.add(person);
                }
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("reslutList.size:"+ resultList.size());
        System.out.println("10W*1W次循環耗時:"+ (end - start) + "毫秒");
        System.out.println("----------------------------");
 }

 /**
  * 使用Map和List的特性進行匹配:
  * Map為key-value結構,不能放重復數據
  * List可以放重復數據
  * 使用String型id做key,List<Person>做value
  * 遍歷List<String>, map.get(String)則取出id == str 的List
  */
 private void  testMapAndList(){
  //構造一個10萬個Person對象的list
  List<Person> personList = new ArrayList<Person>();

  for (int i = 0; i < 100000; i++) {
   int j =10000;
   personList.add(new Person((i%j) +"", "張三"+i));
  }
  //構造一個1萬個String對象的list
  List<String> strList = new ArrayList<String>();
  for (int i = 0; i < 10000; i++) {
   strList.add(i +"");
  }

  long start = System.currentTimeMillis();
  //利用Map和List的特性整理數據
        Map<String, List<Person>> map = new HashMap<String, List<Person>>();
        //將10W條數據根據id放入map
        for(int i=0;i<personList.size();i++){
         Person person = personList.get(i);
             String id = person.getId();
              if(map.containsKey(id)){
                  map.get(id).add(person);
              }else{
                   List<Person> pList = new ArrayList<Person>();
                   pList.add(person);
                   //id為key,相同id的person的List為value
                   map.put(id,pList);
             }
        }
      //保存匹配結果
        List<Person> resultList = new ArrayList<Person>();
        //根據1W條str,map.get(str)取匹配上的數據
        for (String str : strList) {
            List<Person> pList = map.get(str);
            if (pList != null) {
                resultList.addAll(pList);
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("map.size:" +map.size());
        System.out.println("reslutList.size:"+ resultList.size());
        System.out.println("10W+1W次循環耗時:"+ (end - start) + "毫秒");
  System.out.println("----------------------------");
 }
}
/**
 * Person實體類
 */
class Person{
 private String id;
 private String name;
 public Person() {}

 public Person(String id, String name) {
  this.id = id;
  this.name = name;
 }
 @Override
 public String toString() {
  return this.id +"::>"+ this.name;
 }

 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
}

 

測試結果:

============開始=============
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多陪,達到了預想的目的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM