2012-04-28 3 views
1

У меня есть большая коллекция текстовых данных с разделителями вкладок в форме DATE NAME MESSAGE. По большому счету, я имею в виду набор из 1.76GB, разделенный на 1075 файлов. Я должен получить данные от все файлы. До сих пор у меня есть следующее:Строка, выделенная из памяти

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.Кроме того, одни и те же пользователи группируются вместе, но могут быть разделены на два отдельных файла линейно. – javaCity

+0

Какая у вас ОС? – 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. Если возможно, устранить хранения всех ваших NAME значений в userCount ArrayList. Как предложил А. Р. С., вы можете сначала записать эту информацию в другой файл, а затем просто прочитать файл, когда вам нужно снова вытащить эти данные. Если это не привлекательный вариант, вы все равно можете записать свою информацию в OutputStream, который затем будет передан в InputStream в другом месте вашего приложения. Это сохранит ваши данные в памяти, но везде, где вы используете список значений NAME, можно начинать обработку/отображение/независимо друг от друга, так как вы продолжаете читать эти 1000+ файлов, ища больше значений NAME.

  3. Используйте метод listFiles(FileFilter), поэтому Java может отфильтровывать нетекстовые файлы для вас. Это должно предотвратить несколько дополнительных циклов процессора, так как вам больше не придется перебирать файлы с неправильным расширением, прежде чем устранять их.
1

String.split возвращает строки, которые используют внутри того же массива символов, что и исходная строка. Неиспользованные символы не будут собирать мусор.

Попробуйте использовать новую строку (str.split ("\ t") [1]), чтобы принудительно назначить новый массив.

+0

Я пробовал это, но он тоже не работает. Когда вы выполняете разделение, внутри есть массив, содержащий все части исходной строки. Так что это не имеет никакого значения. Но, спасибо за комментарий. – javaCity

+0

По крайней мере, это не всегда верно (например, в jdk1.7). –

Смежные вопросы