Токенизация больших файлов в хеш-таблицу в Java - PullRequest
4 голосов
/ 05 ноября 2011

У меня проблема: я читаю 900 файлов, и после обработки файлов мой окончательный вывод будет HashMap<String, <HashMap<String, Double>>.Первая строка - fileName, вторая - слово, а двойная - частота слова.Порядок обработки следующий:

  • чтение первого файла
    • чтение первой строки файла
    • разбиение важных токенов на массив строк
    • скопировать массив строк в мою последнюю карту, увеличивая частоты слов
  • повторить для всех файлов

Я использую строку BufferedReader.Проблема в том, что после обработки первых файлов хэш становится настолько большим, что через некоторое время производительность становится очень низкой.Я хотел бы услышать решение для этого.Моя идея состоит в том, чтобы создать ограниченный хэш, после того как достигнут предел хранения в файле.делайте так, пока все не будет обработано, в конце смешайте все хэши.

Ответы [ 5 ]

1 голос
/ 05 ноября 2011

Время обработки хеш-карты не должно значительно увеличиваться по мере роста. Возможно, ваша карта искажается из-за неподходящей функции хеширования или слишком большого заполнения. Если вы не используете больше оперативной памяти, чем вы можете получить из системы, вам не нужно разбивать вещи.

Что я видел в Java при запуске огромных хеш-карт (или любой коллекции) с большим количеством объектов в памяти, так это то, что виртуальная машина сходит с ума при попытке запустить сборщик мусора. Он доходит до того, что 90% времени тратится на то, чтобы JVM запускала сборщик мусора, что занимает некоторое время и обнаруживает, что почти каждый объект имеет ссылку.

Я предлагаю профилировать ваше приложение, и если это сборщик мусора, то увеличение пространства кучи и настройка сборщика мусора. Кроме того, это поможет, если вы сможете приблизить необходимый размер ваших хеш-карт и обеспечить достаточно большие выделения (см. Параметры initialCapacity и loadFactor в конструкторе).

1 голос
/ 05 ноября 2011

Почему бы просто не читать по одному файлу за раз, записывать результаты этого файла на диск, а затем читать следующий файл и т. Д.?Очевидно, что каждый файл не зависит от других с точки зрения сопоставления, так зачем сохранять результаты первого файла, пока вы пишете второй?

Вы могли бы записать результаты для каждого файла в другой файл (например, foo.txt => foo.txt.map), или вы можете создать один файл с каким-то разделителем между результатами, например

==== foo.txt ====
word - 1
the - 3
get - 3
==== bar.txt ====
apple - 2
// etc

Кстати, почему вы используете double дляЧастота?Конечно, это должно быть целочисленное значение ...

0 голосов
/ 05 ноября 2011

Почему бы не использовать пользовательский класс,

public class CustomData {
 private String word;
 private double frequency;
 //Setters and Getters
}

и используйте свою карту как

Map<fileName, List<CustomData>>

так, по крайней мере, у вас будет только 900 ключей на карте.

-Ivar

0 голосов
/ 05 ноября 2011

Я пытаюсь переосмыслить вашу проблему:

Поскольку вы пытаетесь построить инвертированный индекс:

  1. Используйте Multimap вместо Map<String, Map<String, Integer>>

    Multimap<word, frequency, fileName, .some thing else tomorrow>

  2. Теперь прочитайте один файл, создайте Multimap и сохраните его на диске. (аналогично ответу Джона)

  3. После прочтения x файлов объедините все мультикарты вместе: putAll(multimap), если вам действительно нужна одна общая карта всех значений.

0 голосов
/ 05 ноября 2011

Вы можете попробовать использовать эту библиотеку для повышения вашей производительности.

http://high -scale-lib.sourceforge.net /

Это похоже на API Java-коллекций, но для высокой производительности. Было бы идеально, если бы вы могли группировать и объединять эти результаты после обработки их небольшими партиями.

Вот статья, которая поможет вам с некоторыми дополнительными данными.

http://www.javaspecialists.eu/archive/Issue193.html

...