2012-04-28 3 views
1

私は、タブで区切られた大量のテキストデータをDATE NAME MESSAGEという形で持っています。大きな意味では、1.76GBのコレクションが1075個の実際のファイルに分割されています。 NAMEのデータをからすべてのファイルに取得する必要があります。これまで私はこれを持っています:文字列がメモリから分割される

File f = new File(directory); 
     File files[] = f.listFiles(); 
     // HashSet<String> all = new HashSet<String>(); 
     ArrayList<String> userCount = new ArrayList<String>(); 
     for (File file : files) { 
      if (file.getName().endsWith(".txt")) { 
       System.out.println(file.getName()); 
       BufferedReader in; 
       try { 
        in = new BufferedReader(new FileReader(file)); 
        String str; 
        while ((str = in.readLine()) != null) { 
         // if (all.add(str)) { 
         userCount.add(str.split("\t")[1]); 
         // } 

         // if (all.size() > 500) 
         // all.clear(); 
        } 
        in.close(); 
       } catch (IOException e) { 
        System.err.println("Something went wrong: " 
          + e.getMessage()); 
       } 

      } 
     } 

私のプログラムは、常に-Xmx1700でもメモリ例外を出しています。私はそれを超えて行くことはできません。とにかくArrayList<String>NAMEを処理できるようにコードを最適化できますか?

+2

は、この必要性は、Javaプログラムであることをしていますか?これにLinux/Unixツールを使用できますか? Windowsのバッチ? – Marc

+0

それはJavaプログラムである必要はありませんが、私はこれのためにLinuxツールを使用する方法を知らない。ここでの目標は、ユーザーあたりのメッセージ数を1〜200の間でカウントすることです。また、同じユーザーは一緒にクラスタ化されますが、2つの別々のファイルに線形に分割できます。 – javaCity

+0

あなたのOSは? – Marc

答えて

3

Java以外の代替ソリューションを使用できるように思われるので、ここではそれを処理する必要があるawkファイルがあります。

cat *.txt | awk -F'\t' '{sum[$2] += 1} END {for (name in sum) print name "," sum[name]}' 

説明:

-F'\t' - separate on tabs 
sum[$2] += 1 - increment the value for the second element (name) 

連想配列は、これは非常に簡潔にします。次のように私が作成したテストファイルでそれを実行:

import random 

def main(): 
    names = ['Nick', 'Frances', 'Carl'] 
    for i in range(10000): 
     date = '2012-03-24' 
     name = random.choice(names) 
     message = 'asdf' 
     print '%s\t%s\t%s' %(date, name, message) 

if __name__ == '__main__': 
    main() 

私は結果を得る:

Carl,3388 
Frances,3277 
Nick,3335 
+0

素晴らしい。これは私を助けた!私は、Linuxスクリプトについてもっと知る必要があります。どうもありがとうございました。また、説明に感謝します。 – javaCity

+2

'grep'と' awk'は非常に強力です。それらを学び、繁栄する。 –

1

あなたはメモリフットプリントと、コードの一般的なパフォーマンスを向上させるためにできることがいくつかあります:

  1. は、次のものに移動する前に、あなたのFileReaderのオブジェクトを閉じます。 FileReaderはInputStreamReaderであり、リソースを解放するためにclose()を呼び出す必要があります。あなたの現在のコードは、あなたが見ているすべてのファイルに対してストリームを有効に保っています。

    for(File file: files) { 
        BufferedReader in = null; 
        try{ 
         in = new BufferedReader(new FileReader(file)); 
         // TODO do whatever you want here. 
        } 
        finally{ 
         if(in != null) { 
          in.close(); 
         } 
        } 
    } 
    
  2. 可能であれば、userCount ArrayListの中であなたのNAMEすべての値を格納排除します。 A.R.S.が示唆したように、この情報を別のファイルに書き込んでから、そのデータを再度プルする必要があるときにファイルを読み取ることができます。それが魅力的ではない場合は、あなたの情報をOutputStreamに書き込むことができます。この情報は、アプリケーション内の他の場所のInputStreamにパイプされます。これにより、データがメモリに保持されますが、NAME値のリストを使用している場合は、これらの1,000以上のファイルを引き続き読んで、さらにNAMEの値を検索すると、値の処理/表示/

  3. listFiles(FileFilter)メソッドを使用すると、Javaが非テキストファイルをフィルタリングすることができます。これにより、間違った拡張子を持つファイルを反復処理する必要がなくなるため、CPUサイクルが余分に使われなくなります。
1

String.splitは、元のStringと同じ文字配列を内部的に使用するStringsを返します。未使用の文字はガベージコレクションされません。

新しい文字列(str.split( "\ t")[1])を使用して、新しい配列の割り当てを強制してください。

+0

これを試しましたが、うまくいきません。分割するとき、内部には元の文字列のすべての部分を含む配列があります。だから違いはありません。しかし、コメントをいただきありがとうございます。 – javaCity

+0

少なくともそれは必ずしも真ではありません(例えば、jdk1.7など)。 –