Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Java vs Go

Anna | 4.06.2014 | нет комментариев

Последнее время язык Go стал дюже обговариваемым, и достаточно Зачастую его сопоставляют с Java. Неделя Go принесла нам дюже увлекательную ознакомительную статью Dreadd, и мне стало увлекательно, как совладает с описанной задачей Java.
По мере написания кода стало ясно, что и в Java тоже есть много увлекательного, но немного освещённого в прессе. Я постарался применять самые увлекательные нововведения из Java7, верю здесь обнаружат пригодную информацию как начинающие, так и опытные, но ленивые Java разработчики.

Задача

Задача была взята без изменений, и решить её испробуем как дозволено больше близким к оригиналу методом. У нас верно так же будет несколько потоков чтения данных, один поток сохранения, оповещение по таймеру и по закрытию программы. Параметры будем получать из командной строки при запуске.

Подлинная постановка задачи

… неотложно, под покровом темноты, загрузить себе полный дамп всех цитат на модерации[http://vpustotu.ru/moderation/] для последующего тайного исследования…

Таким образом необходима программа, которая:

  1. Должна ступенчато обновлять и парсить (разбирать) страницу, записывая цитату.
  2. Должна уметь отбрасывать дубликаты.
  3. Должна останавливаться не только по команде, но и по достижению определенного числа “повторов”, скажем 500!
  4. Так как это, скорее каждого, займет некоторое время: нужно уметь продолжить “с места на котором остановились” позже закрытия.
  5. Ну и раз уж все-таки это надолго – пускай делает свое чумазое дело в несколько потоков. Отлично-бы в целых 4 потока (либо даже 5!).
  6. И отчитывается об фурорах в консоль всякие, скажем, 10 секунд.
  7. А все эти параметры пускай принимает из доводов командной строки!

 

Параметры командной строки

Начнём, как в подлинной статье, с начала, т.е. с разбора параметров. Стандартной библиотеки для этих целей в Java нет, но сторонние есть на всякий вкус. Мне нравится jcommander. Решение, как говорится, “java way”.

private static class CommandLine {
	@Parameter(names = "-h", help = true)
	boolean help;

	@Parameter(names = "-w", description = "число потоков")
	int workers = 2;

	@Parameter(names = "-r", description = "частота отчетов (сек)")
	int reportPeriod = 10;

	@Parameter(names = "-d", description = "кол-во дубликатов для остановки")
	int dupToStop = 500;

	@Parameter(names = "-hf", description = "файл хешей")
	String hashFile = "hash.bin";

	@Parameter(names = "-qf", description = "файл записей")
	String quotesFile = "quotes.txt";
}
...
CommandLine commandLine = new CommandLine(); //тут будут переданные доводы
JCommander commander = new JCommander(commandLine, args); //вот такой нетипичный вызов	
if (commandLine.help) commander.usage(); //вызов справки нужно обрабатывать вручную, недоработочка...

 

То же на Go

var (
	WORKERS       int             = 2                     //кол-во "потоков"
	REPORT_PERIOD int             = 10                    //частота отчетов (сек)
	DUP_TO_STOP   int             = 500                   //максимум повторов до останова
	HASH_FILE     string          = "hash.bin"            //файл с хешами
	QUOTES_FILE   string          = "quotes.txt"          //файл с цитатами
	used          map[string]bool = make(map[string]bool) //map в котором в качестве ключей будем применять строки, а для значений - булев тип.
)

func init() {
	//Задаем правила разбора:
	flag.IntVar(&WORKERS, "w", WORKERS, "число потоков")
	flag.IntVar(&REPORT_PERIOD, "r", REPORT_PERIOD, "частота отчетов (сек)")
	flag.IntVar(&DUP_TO_STOP, "d", DUP_TO_STOP, "кол-во дубликатов для остановки")
	flag.StringVar(&HASH_FILE, "hf", HASH_FILE, "файл хешей")
	flag.StringVar(&QUOTES_FILE, "qf", QUOTES_FILE, "файл записей")
	//И запускаем разбор доводов
	flag.Parse()
}

Аннотации всякий код делают отменнее.

Каналы

В Go для передачи цитат применялись каналы, в Java мы возьмём ближайший аналог — BlockingQueue:

BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);

Мы не сумеем читать из нескольких очередей в одном потоке. Но у нас есть другие плюшки, скажем можем ограничить длину очереди, если не будем поспевать её разгрести.
Горутин у нас нет, а есть Runnable. Безусловно досадно создавать объект ради одного способа, но это дело тезиса.

new Thread(new Grabber()).start();

Да, достаточно многословно, не поспоришь, но это не предел.

Thread worker = new Thread(new Grabber());
worker.setPriority(2);
worker.setDaemon(true);
worker.start();

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

Парсинг HTML

Что касается вызываемого способа, здесь теснее отменнее.

public class Grabber implements Runnable{
...
	public void run() {
		try {
			while (true) {  //в нерушимом цикле собираем данные
				Document doc = Jsoup.connect("http://vpustotu.ru/moderation/").get();
				Element element = doc.getElementsByClass("fi_text").first();
				if (element != null){
					queue.put(element.text()); //и отправляем их в очередь
				}
			}
		} catch (IOException | InterruptedException e) { 
			e.printStackTrace();
		}		
	}

 

То же на Go

func() {
	for { //в нерушимом цикле собираем данные
		x, err := goquery.ParseUrl("http://vpustotu.ru/moderation/")
			if err == nil {
				if s := strings.TrimSpace(x.Find(".fi_text").Text()); s != "" {
					c <- s //и отправляем их в канал
				}
			}
		time.Sleep(100 * time.Millisecond)
	}
}

В тезисе содержимое способа подобно. Для парсинга HTML так же применяется внешняя библиотека, достаточно славная jsoup. Всяко комфортнее встроенного в swing.
Многие гнобят Java за массивную обработку исключений, но применение для этого if err == nil в Go легко страшно. И в Java дозволено отказаться от обработки, чем мы и воспользуемся в дальнейшем примере.

Работа с файлами

Работа с файлами так же достаточно так же достаточно схожа. Обращу внимание на новые неблокирующие классы для работы с файлами из Java7. Обнаружить из дозволено в java.nio, применение примерно всецело совпадает с аналогом в Go:

//открытие файла на чтение
InputStream hashStream = Files.newInputStream(Paths.get(commandLine.hashFile)
//открытие файла на запись
OutputStream hashFile = Files.newOutputStream(Paths.get(commandLine.hashFile), CREATE, APPEND, WRITE);

 

То же на Go

//открытие файла на чтение
hash_file, err := os.OpenFile(HASH_FILE, os.O_RDONLY, 0666)
//открытие файла на запись
hash_file, err := os.OpenFile(HASH_FILE, os.O_APPEND|os.O_CREATE, 0666)

В Java, как я и обещал, дозволено отказаться от очевидной обработки ошибок.

public static void main(String[] args) throws IOException
try-resource

Мне дюже понравился оператор defer в Go, кто пробовал закрыть поток в finally, должен оценить. Но к счастью мы спасены, и в Java7 добавлена конструкция try-resource.

try (
	OutputStream hashFile = Files.newOutputStream(Paths.get(commandLine.hashFile), CREATE, APPEND, WRITE);
	InputStream hashStream = Files.newInputStream(Paths.get(commandLine.hashFile));
	BufferedWriter quotesFile = Files.newBufferedWriter(Paths.get(commandLine.quotesFile), 
			Charset.forName("UTF8"), CREATE, APPEND, WRITE);) {
...
}

Упомянутые в скобочках объекты обязаны реализовывать интерфейс java.io.Closeable, и они будут закрыты по окончанию блока try. Да, крепко смахивает на костыль, но не менее комфортно, чем defer.

Сопоставление хешей

Отдельно дозволено обратить внимание на перевод массива байт в строку.

Hex.encodeHexString(hash);

В стандартной библиотеке такого способа нет, для идентичности оригиналу я применял библиотеку apache commons codec. Но один способ дозволено было и написать самому.

static String encodeHexString(byte[] a) {
	StringBuilder sb = new StringBuilder();
	for (byte b : a)
		sb.append(String.format("%02x", b & 0xff));
	return sb.toString();
}

На самом деле он здесь не необходим, чай не значимо в какой кодировке сберегать массив байт, это может быть и UTF-16 к примеру.

new String(hash, "UTF16");

А дозволено и не кодировать вовсе, необходим только Comparator для сопоставления массивов. Скажем такой.

static Set<byte[]> hashes = new TreeSet<>(new Comparator<byte[]>() {
	public int compare(byte[] a1, byte[] a2) {
		int result = a1.length - a2.length;
		if (result == 0){
			for (int i = 0; i < a1.length; i  ){
				result = a1[i] - a2[i];
				if (result != 0) break;
			}
		}
		return result;
	};
});
Вспомогательные потоки

Основным потоком теснее управляет очередь цитат, значит оповещения обязаны трудиться в своих потоках сами. Помимо этого момента отличий в коде примерно нет.
Закрытие обработаем с поддержкой shuldownHook’а.

Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
		System.out.printf("Завершаю работу. Каждого записей: "   hashes.size());
	}
});

Таймер возьмём у swing.

new Timer(commandLine.reportPeriod * 1000, new ActionListener() {				
	@Override
	public void actionPerformed(ActionEvent arg0) {
		System.out.printf("Каждого %d / Повторов %d (%d записей/сек) n", hashes.size(), dupCount, quotesCount/commandLine.reportPeriod);
		quotesCount = 0;
	}
}).start();

Дабы иметь доступ к dupCount и quotesCount пришлось их перенести из способа в признаки класса, но на работу с ними это не повлияло.

Обнаружить полный код дозволено здесь:
http://pastebin.com/pLLVxTXZ

Итог

Что увлекательно, объём программ в строчках оказался приблизительно идентичный. Читаемость, на мой взор тоже схожая, но это дозволено оценить только со стороны. В одном языке что-то сделано комфортнее, в ином — другое, и однозначно выделить какой-то язык я не могу. Но это достаточно малое приложение исходного яруса, и было бы увлекательно сравнить языки и подходы в масштабных Enterprise решениях.

Спасибо за внимание.

Источник: programmingmaster.ru

Оставить комментарий
БАЗА ЗНАНИЙ
СЛУЧАЙНАЯ СТАТЬЯ
СЛУЧАЙНЫЙ БЛОГ
СЛУЧАЙНЫЙ МОД
СЛУЧАЙНЫЙ СКИН
НОВЫЕ МОДЫ
НОВЫЕ СКИНЫ
НАКОПЛЕННЫЙ ОПЫТ
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB