2017-10-23 1 views
1

문자열 테이블을 한 형식에서 다른 형식으로 변환하는 작업이 있습니다. enter image description here테이블 변환 알고리즘의 속도를 향상시키는 방법은 무엇입니까?

나는 테이블 변환이 클래스를 사용

import java.util.ArrayList; 
import java.util.Date; 
import java.util.HashMap; 
import java.util.Map; 

class TableConverter 
{ 
    public String[] Entities; //here I store descriptive fields like DescField1, DescField2... 
    public ArrayList<String> ConvertedList; //here I store converted table rows as separate string lines 

    public TableConverter(ArrayList<String> lines) //we receive table rows as separate string lines 
    { 
     String[] splitted_first_line = lines.get(0).split("\t"); //split first row to get descriptive fields 
     this.Entities = new String[splitted_first_line.length - 2]; //allocate size to hold all descriptive fields. -2 because last two columns is Date and Total 
     System.arraycopy(splitted_first_line, 0, this.Entities, 0, this.Entities.length); //copy descriptive fields into my arr  

     //-- 

     int lines_sz = lines.size(); //save lines size to not recalculate it every iteration 
     Map<String, Integer> k_d_map = new HashMap<String, Integer>(); //map to store indecies of every Date column 

     for (int i = 1; i < lines_sz; i++) 
     { 
      if (lines.get(i).isEmpty()) 
       continue; 

      String[] splitted_line = lines.get(i).split("\t"); //splitted line on values  

      if (!k_d_map.containsKey(splitted_line[splitted_line.length - 2])) //if my map does not contain such date 
       k_d_map.put(splitted_line[splitted_line.length - 2], 0); //then add it 
     } 

     String[] known_dates = k_d_map.keySet().toArray(new String[k_d_map.size()]); 
     SortStrDates(known_dates); //I sort dates by ASC 
     k_d_map.clear(); //clear map to fill it again with correct indecies 

     for (int i = 0; i < known_dates.length; i++) //refilling map and now we know every date index 
      k_d_map.put(known_dates[i], i); 

     //-- 

     Map<String, EntitySales> ESs_map = new HashMap<String, EntitySales>(); //map for rows 

     for (int i = 1; i < lines_sz; i++) 
     { 
      if (lines.get(i).isEmpty()) 
       continue; 

      String[] splitted_line = lines.get(i).split("\t"); //split row 
      String curr_entity = GetEntityFromLine(splitted_line); //I get set of descriptive fields separated by \t. It looks like this: asd\tqwe\t...\tzxc 
      int dti = k_d_map.get(splitted_line[splitted_line.length - 2]); //I get date column index for Date stored in this row (if it was 02.2017 then index will be 0) 

      if (ESs_map.containsKey(curr_entity)) //I check if map contains row with such descriptive fields set 
       ESs_map.get(curr_entity).SalesAmounts[dti] = splitted_line[splitted_line.length - 1]; //if contains, we set sale amount at date index (set 5 to 02.2017 column for example) 
      else 
      { 
       EntitySales es = new EntitySales(curr_entity, known_dates.length); //else we create new object to hold row   
       es.SalesAmounts[dti] = splitted_line[splitted_line.length - 1]; //set sales amount at date 
       ESs_map.put(curr_entity, es); //and add to map 
      } 
     } 

     //-- 

     String first_row = ""; //here and below I build first row text representation, I add stored DescFields and unique dates 
     this.ConvertedList = new ArrayList<String>();    

     for (int i = 0; i < this.Entities.length; i++) 
      first_row += this.Entities[i] + "\t"; 

     for (int i = 0; i < known_dates.length; i++) 
      first_row += i < known_dates.length - 1 ? known_dates[i] + "\t" : known_dates[i]; 

     this.ConvertedList.add(first_row); 

     //-- 

     for (EntitySales es : ESs_map.values()) //Here I get rows as separate lines 
      this.ConvertedList.add(es.GetAsLine()); 
    } 

    public String GetEntityFromLine(String[] line) 
    { 
     String[] entities = new String[line.length - 2]; 
     System.arraycopy(line, 0, entities, 0, entities.length); 

     String entity = ""; 

     for (int i = 0; i < entities.length; i++) 
      entity += i < entities.length - 1 ? entities[i] + "\t" : entities[i]; 

     return entity; 
    } 

    public void SortStrDates(String[] dates) 
    { 
     for (int i = 0; i < dates.length; i++) 
      for (int j = i + 1; j < dates.length; j++) 
      { 
       Date dt_i = MyJunk.ConvertStrToDate(dates[i]); 
       Date dt_j = MyJunk.ConvertStrToDate(dates[j]); 

       if (dt_j.before(dt_i)) 
       { 
        String temp_i = dates[i]; 
        dates[i] = dates[j]; 
        dates[j] = temp_i; 
       } 
      } 
    } 
} 

class EntitySales 
{ 
    public String Entity; 
    public String[] SalesAmounts; 

    public EntitySales(String entity, int sales_amounts_size) 
    { 
     this.Entity = entity; 
     this.SalesAmounts = new String[sales_amounts_size]; 
    } 

    public String GetAsLine() 
    { 
     String line = this.Entity + "\t"; 

     for (int i = 0; i < this.SalesAmounts.length; i++) 
     { 
      String val = this.SalesAmounts[i] == null || this.SalesAmounts[i].isEmpty() ? "0" : this.SalesAmounts[i]; 
      line += i < this.SalesAmounts.length - 1 ? val + "\t" : val; 
     } 

     return line; 
    } 
} 

그것은 작동하지만, 큰 테이블이 느린 궁극적. 나는 800k 행 테이블과 취소 된 작업을 변환하기 위해 1 시간 20 분을 기다렸다. 200k 개의 행이 3 분 안에 변환됩니다. 내가 왜 그런 둔화를 겪었는지 모르겠다.하지만 문제는 내 알고리즘을 많이 빠르게하는 방법이다. 설명 필드 (asd \ tqwe \ t ... \ tzxc -> 0, something \ telse -> 1)의 모든 집합에 Integer 값을 할당하고 Maps없이 해당 정수를 비교하려고 시도했지만 속도가 느려졌습니다. 당신이 당신의 전반적인 알고리즘을 개선 할 수있는 반면

+0

작은 개선 사항 : for 루프 내에 문자열 및 문자열 배열을 선언하지 마십시오. 예를 들어, TableConverter 메소드에서'String [] splitted_line'을 한 번만 delcare하여 2 for for 루프에서 사용할 수 있습니다. – StephaneM

+0

@StephaneM - 알았어, 고마워, 슬프게도 나는 모든 일을 너무 빨리 할 수 ​​있다고 생각하지 않는다. – Kosmos

+0

프로파일 러의 감독하에 코드를 실행하는 데 소요되는 시간을 이해하는 데 도움이 될 수 있습니다. – fvu

답변

1

, 기본 둔화는 GetAsLine 기능에 아마 다음은

public String GetAsLine() 
{ 
    String line = this.Entity + "\t"; 

    for (int i = 0; i < this.SalesAmounts.length; i++) 
    { 
     String val = this.SalesAmounts[i] == null || this.SalesAmounts[i].isEmpty() ? "0" : this.SalesAmounts[i]; 
     line += i < this.SalesAmounts.length - 1 ? val + "\t" : val; 
    } 

    return line; 
} 

, 당신이 당신의 키를 구축하는 루프에서 문자열 연결을 사용하고 있습니다. 이것은 루프를 통해 매번 새로운 문자열을 할당하기 때문에 매우 비효율적입니다. 여기에는 새 문자열에 메모리를 할당하고 기존 문자열을 새 문자열에 복사하는 작업이 포함됩니다. 가비지 컬렉터는 많은 운동을합니다.

이를 개선하기 위해 무엇을 당신이 원하는 것은 거기에 문자열을 StringBuilder를 작성하고 구성입니다 : StringBuilder 때마다 새로운 캐릭터를 생성하지 않기 때문에

StringBuilder line = new StringBuilder(); 
for (int i = 0; i < this.SalesAmounts.length; i++) 
{ 
    String val = this.SalesAmounts[i] == null || this.SalesAmounts[i].isEmpty() ? "0" : this.SalesAmounts[i]; 
    line.append(val+"\t"); 
} 
// remove final tab character 
line.remove(line.length()-1, line.length()-1); 

return line.toString(); 

이 빠른 이유는 당신 뭔가를 추가하십시오. 따라서 문자열 복사가 훨씬 줄어 듭니다.

관련 문제