Доброе всем время суток. Есть задача:
Исходные данные:
Входной поток — набор слов. Для простоты предполагается, что данные хранятся в текстовом файле, но при этом общее количество слов и размер файла заранее неизвестны, т.е. нельзя прочитать в память весь файл целиком. Кодировка — Задается.
Необходимо выполнить разбора входного текста на слова и вычислить частоту встречаемости каждого слова. В качестве "слова" понимается набор символов, состоящий из букв, цифр и символа подчеркивания.
Все остальные символы считаются разделителями.
При проектировании следует исходить из предположения, что создаваемый код будет использоваться в рамках реальной системы.
Требования: расширяемость, производительность, экономия памяти
Результат:
Вывести в текстовый файл список слов со значениями частоты встречаемости.
Должна быть предусмотрена возможность вывода списка, отсортированного по частоте.
Сортировка по словам не требуется.
Язык реализации: Java.
Есть вот такая реализация этой задачи:
Основной класс
package parser;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.Properties;
/**
*
* @author Alexander
* Main Class
*
*/
public class Parser {
private static String inputFilePath;
private static String outputFilePath;
private static String encoding;
/**
* @param args
*/
public static void main(String[] args) {
InputStream is = Object.class.getResourceAsStream("/regexp.properties");
Properties props = new Properties();
try {
//load properties
props.load(is);
String regexpString = props.getProperty("regexp");
TextParser.setRegexp(regexpString);
String maxBuffSize = props.getProperty("maxBufferSize");
TextParser.setMAX_BUFF_SIZE(Integer.parseInt(maxBuffSize));
inputFilePath = props.getProperty("inputFilePath");
outputFilePath = props.getProperty("outPutFilePath");
encoding = props.getProperty("encoding");
} catch (IOException e) {
e.printStackTrace();
System.err.println("no properties");
System.exit(666);
}
BufferedReader breader;
try {
System.out.println("start work");
//open file with name=inputFileName and with encoding=encoding
breader = new BufferedReader(new InputStreamReader(
new FileInputStream(inputFilePath), encoding));
TextParser.textParsing(breader);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(outputFilePath), encoding));
ParserUtil.GenerateReport(writer);
System.out.println("complete work");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
Он собственно загружает проперти и открывает потоки.
Есть класс утиль, в котором содержатся утилитарные методы
package parser;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
/**
*
* @author Alexander
* Class with utilits methods
*/
public class ParserUtil {
//Map for storing lexems: String - lexem, Integer - count
static HashMap<String, Integer> countsOfWords = new HashMap<String, Integer>();
//sortin map by value
private static List SortMap(HashMap<String, Integer> map) {
List entryList = new ArrayList(map.entrySet());
Collections.sort(entryList, new ValuesComparator());
return entryList;
}
//method for adding lexem to the map
public static void AddWordToHashMap(String newWord) {
if (countsOfWords.containsKey(newWord)) {
Integer count = countsOfWords.get(newWord);
count++;
countsOfWords.put(newWord, count);
} else {
Integer count = 1;
countsOfWords.put(newWord, count);
}
}
//write result into the file
public static void GenerateReport(BufferedWriter writer) throws IOException {
List entryList = ParserUtil.SortMap(countsOfWords);
Entry<String, Integer> entry;
for (Iterator itr = entryList.iterator(); itr.hasNext();) {
entry = (Entry<String, Integer>) itr.next();
writer.write(entry.getKey() + "=" + entry.getValue() + " ");
}
writer.close();
}
}
Класс в котором производится парсинг текста.
package parser;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author Alexander
* Class for text parsing
*
*/
public class TextParser {
private static int MAX_BUFF_SIZE;
private static String regexp;
private static boolean partFlag;
private static String completeString;
/**
* This method load data from txt file into the buffer and use
* java.util.regex.Matcher, and java.util.regex.Pattern for finding lexems
*
* @param breader -
* buffered reader
* @throws IOException
*/
public static void textParsing(BufferedReader breader) throws IOException {
// compile regular expresion
Pattern lexem = Pattern.compile(regexp);
int bufferCount;
// word - founded lexem
String word = new String();
// part of lexem
String tmpString = new String();
char[] charBuffer = new char[MAX_BUFF_SIZE];
partFlag = false;
// reads chars from file into the buffer
// bufferCount - count of chars wich we had read
while ((bufferCount = breader.read(charBuffer)) != -1) {
// create new string
String str = new String(charBuffer, 0, bufferCount);
// use matcher for finding lexems
Matcher mat = lexem.matcher(str);
while (mat.find()) {
// find lexem
word = mat.group();
// if lexem < then our buffer and there is no lexem's part in
// buffer
if ((mat.end() < MAX_BUFF_SIZE) && !partFlag) {
// we founded lexem
completeString = mat.group();
} else {
// find secod part of the lexem
if ((mat.start() == 0) && partFlag
&& (mat.end() != MAX_BUFF_SIZE)) {
completeString = tmpString + mat.group();
partFlag = false;
} else {
// if second part > then our buffer
if ((mat.start() == 0) && partFlag
&& (mat.end() == MAX_BUFF_SIZE)) {
String secondPart = new String(tmpString);
tmpString = secondPart + mat.group();
partFlag = true;
} else {
// if lexem > then our buffer
if ((mat.end() == MAX_BUFF_SIZE)) {
// set flag
partFlag = true;
// store part of the string
tmpString = word;
} else {
completeString = tmpString;
partFlag = false;
}
}
}
}
if (!partFlag) {
ParserUtil.AddWordToHashMap(completeString);
}
}
}
breader.close();
}
public static void setMAX_BUFF_SIZE(int max_buff_size) {
MAX_BUFF_SIZE = max_buff_size;
}
public static void setRegexp(String regexp) {
TextParser.regexp = regexp;
}
}
И последний класс — который позволяет сортировать мэп по валуе
package parser;
import java.util.Comparator;
import java.util.*;
/**
*
* @author Alexander
* Class for compare values from hash map
*
*/
public class ValuesComparator implements Comparator {
public int compare(Object arg0, Object arg1) {
Map.Entry first = (Map.Entry)arg0;
Map.Entry second = (Map.Entry)arg1;
Comparable comparableFirst = (Comparable)first.getValue();
Comparable comparableSecond = (Comparable)second.getValue();
return comparableFirst.compareTo(comparableSecond);
}
}
И наконец — файл с пропертис
regexp=[\\w+[\\p{L}+[\\d+]]]+
inputFilePath=D:\\Eclipse\\Test\\1.txt
outPutFilePath=rezult.txt
maxBufferSize=10
encoding=KOI8-R
Эта реализация работает. Все парсит. Подскажите пожалуйста — как в этой реализации повысить производительность? Основные требования — "Требования: расширяемость, производительность, экономия памяти". Как эти параметры соблюсти? может есть какие-нибудь приемы или паттерны для похожих задач?