Печать наиболее часто используемых слов из фраз. Самый эффективный способ заполнения вектора целых чисел без дубликатов. Обратная обратная связь, запрашиваемая при простых путях. Проверка на palindrome. Составление соответствующих записей в большом файле биоинформатики. Код кода 2017. День 8. Выполнение простых инструкций. Преобразование целого числа в виде квадрата в целое число с нулевым значением. потенциально различный типCompute в день года. Правильный способ проверки с субтитра на символ SubMoving в Unity3D на основе ввода акселерометра. WinForms dice rollerException handling и др. - Как сделать этот веб-загрузчик не «бедным»? Порядок доения совместим с максимальным количеством наблюдений. pangrams в CReplace '' Пробелы в URL-адресе с «% 20» - Кодирование ChallengeTemperature conversion codeSolving Project Euler Проблема 1 с использованием методов расширенияMultithreaded Generator Mandelbrot Ver 2Простой калькулятор Java с использованием SwingWinforms Circular CheckboxУчисление количества символов вхожденияHackerrank «Шерлок Холмс» challengeThres hed и Malachi'd: Решетка EratosthenesValidate и импортировать данные из шаблонов вычисления размерности Euclidean в формате Excel. Установка «модов» с помощью слов PythonReversing в функции stringMin /Max 1D массива в C /C ++. Создание последовательности цифр, которые появляются в последовательности натуральных чисел. Развертывание при циклировании по объектной модели, поступающей из игры 3rd partPong с использованием моделирования SDLDoodlebug и ant. Метод прогонаROT47. Реализация. Простой калькулятор калькулятораCCCCCC ++. Компоновка комбинаций животного kingdomPairwise массива в JavascriptIs. Этот шаблон проектирования хранилища действителен и эффективен? Модуль валидации банковской картыДополнительный способ хранения конфиденциальной информации из зарегистрированной командной строки в Ruby? Short Java FizzBuzz с использованием рекурсии и параметровОптимизация логического метода проверкиUltimate Tic-Tac-Toe ChallengeRepetitive code, приводящий меня в бешенство! Вычисление больших степеней 2 в decimalC ++ Реализация двойного списка ссылок «Угадай ну mber Game "в C
У меня есть набор фраз в List
. Каждая фраза представляет собой String Array
, где каждый элемент в массиве является словом.
Я создаю List<Entry<String, Integer>>
, который содержит words
в качестве ключей и значение times used
. Все сортируется по значению в порядке убывания.
Затем я печатаю слова X
, которые использовались вместе с тем, сколько раз они использовались. Я хочу знать, есть ли лучший /более простой способ сделать это, и вообще все, что вы хотите добавить, чтобы мой код выглядел или работал лучше.
Вот мой код:
public class WordCounting {
public static void printTopWords(final int numberOfWords, List<String[]> phrases) {
List<Entry<String, Integer>> wordsMap = entriesSortedByValues(wordCount(phrases));
Iterator entries = wordsMap.iterator();
int wordsCounter = 1;
while (entries.hasNext() && wordsCounter <= numberOfWords) {
Entry entry = (Entry) entries.next();
String key = (String) entry.getKey();
int value = (int) entry.getValue();
System.out.println(wordsCounter + ": " + key + " - " + value);
wordsCounter++;
}
}
private static Map<String, Integer> wordCount(List<String[]> phrases) {
Map<String, Integer> wordCounter = new TreeMap<>();
for (String[] strings : phrases) {
for (String string : strings) {
wordCounter.put(string, wordCounter.get(string) == null
? 1 : wordCounter.get(string) + 1);
}
}
return wordCounter;
}
static <K, V extends Comparable<? super V>>
List<Entry<K, V>> entriesSortedByValues(Map<K, V> map) {
List<Entry<K, V>> sortedEntries = new ArrayList<>(map.entrySet());
Collections.sort(sortedEntries,
new Comparator<Entry<K, V>>() {
@Override
public int compare(Entry<K, V> e1, Entry<K, V> e2) {
return e2.getValue().compareTo(e1.getValue());
}
}
);
return sortedEntries;
}
}
3 ответа
Как уже отмечали самые очевидные улучшения, я хотел бы поговорить о Java 8. Возможно, вы еще не можете использовать его, но я бы рекомендовал изучить его, и этот ответ должен быть полезен для тех, кто это читает.
Мое основное внимание здесь будет заключаться в разработке кода, чтобы он логически выполнял то, что он должен был делать.
У вас есть метод, в котором вы принимаете аргумент List<String[]>
, и вы хотите вернуть верхние вхождения x, то есть все вы хотите, чтобы это означало:
- Поместите все
String
s вместе в некоторой структуре. - Следите за тем, сколько мест они имеют.
- Сначала сортируйте список, чтобы иметь слова с наивысшим количеством вступлений.
- Верните верхние вхождения x.
- Затем решите, что вы хотите с ним сделать.
Выпуски 4 и 5 здесь отделяются явно, потому что это то, что вы должны делать. Каждый метод должен служить одной цели и делать что-то и , это не одна из целей.
Еще одно улучшение заключается в том, что для ввода я хочу создать структуру, состоящую из String
s, а не вашего (уродливого) List<String[]>
, я также буду иметь дело с этим. Код будет полностью объяснен ниже. Пока мы это делаем, я также улучшу проверку ошибок.
Код:
public static Stream<Map.Entry<String, Long>> getTopWords(final int topX, final Stream<String> words) {
if (topX < 1) {
throw new IllegalArgumentException("invalid value for topX: " + topX);
}
Objects.requireNonNull(words);
Comparator<Map.Entry<String, Long>> comparator = Comparator.comparingLong(Map.Entry::getValue);
return words.collect(Collectors.groupingBy(i -> i, Collectors.counting()))
.entrySet().stream()
.sorted(comparator.reversed())
.limit(topX);
}
List<String[]> phrases = Arrays.asList(new String[]{"a", "b", "c"}, new String[]{"a", "a", "b", "d"});
List<Map.Entry<String, Long>> topEntries = getTopWords(2, phrases.stream().flatMap(Arrays::stream))
.collect(Collectors.toList());
int counter = 1;
for (Map.Entry<String, Long> entry : topEntries) {
System.out.println(counter + ": " + entry.getKey() + " - " + entry.getValue());
counter++;
}
Выход:
1: a - 3
2: b - 2
Объяснение:
- У вас есть ваши фразы
List<String[]>
, вы хотите просто иметь объект, который содержит вашString
s. ЗдесьStream<String>
является подходящим объектом, потому что вам нужно иметь view на вашемphrases
, нет смысла фактически хранить новые данные. Вы делаете это, вызываяphrases.stream().flatMap(Arrays::stream)
.- Сначала вы превратите свой
List<String[]>
вStream<String[]>
. - Затем вы используете ссылку на метод, которая описывает lambda
stringArray -> Arrays.stream(stringArray)
для полученияStream<String>
- Затем с помощью
flatMap
вы добавляете все элементы результирующегоStream<String>
обратно в исходныйStream<String>
.
- Сначала вы превратите свой
- Затем вы начинаете с метода
getTopWords
, который возвращаетStream<Map.Entry<String, Long>>
, чтобы предложить гибкость, чтобы сделать то, что вы хотите с результатами, они не сохраняются еще в точке, где он возвращается. - Сначала я добавил некоторые проверки ошибок.
- Затем я получаю код
Comparator<Map.Entry<String, Long>>
, который будет сравнивать то, что входит в число lower . Это делается с помощьюComparator.comparingLong
по значению записи, которое получается с помощью ссылки на методMap.Entry::getValue
- Затем я запускаю цепочку операций на входе
Stream<String>
:- Сначала создайте результаты по их идентификатору, который обычно создает
Map<String, List<String>>
. - Фокус в том, что я также использовал downstream
Collector
, который подсчитывает количество раз строка, поэтому она называетсяCollectors.counting()
. - В этот момент у меня есть
Map<String, Long>
, обозначающий слово и количество вхождений. Он используетLong
, потому что это то, чтоCollectors.counting()
возвращает. - Затем я получаю код
Set<Map.Entry<String, Long>>
и преобразую его в поток. - Затем я вызываю
sorted()
наStream<Map.Entry<String, Long>>
с помощью обратного компаратора. Это делается здесь, потому что интерференция типа не достаточно сильная, чтобы использоватьComparator.comparingLong(Map.Entry::getValue).reversed()
. - Затем I
limit()
поток сверху xэлементы.
- Сначала создайте результаты по их идентификатору, который обычно создает
- Теперь у нас есть
Stream<Map.Entry<String, Long>>
, и здесь мы решили собрать его вList<Map.Entry<String, Long>>
- Здесь мы продолжим с вашей старой логикой подключения счетчика.
Несколько замечаний, которые стоит отметить:
- Явный
comparator.reversed()
является уродливым, но необходимо, чтобы не приводить к типу casting, что еще более уродливо, это ограничение тока. Это может быть только проблемой в среде IDE, и компилятор javac может действительно скомпилировать его, хотя. - Использование
Map.Entry<String, Long>
довольно раздутое, но наш самый разумный вариант, помимо созданияPair
и используяPair<String, Long>
. Это, надеюсь, будет проще, если Java 9 включает в себя кортежи (которые логически включают пары) в качестве более или менее первоклассных граждан. - В целом метод
getTopWords
мы в конечном итоге сохраняем все записи в памяти один раз с помощьюMap<String, Long>
, я уверен, что есть способы обойти это, но не стоит усилий здесь, только оптимизируйте это, если это станет настоящим узким местом. - Я надеялся использовать метод
Stream.forEach()
при обработке результатов, однако это невозможно с требованием, которое вы хотите иметь a счетчик . Опять-таки, больше возможностей открывается в Java 9, когда мы надеемся, чтоBiStream
s и кортежи.
Я надеюсь, что этот обзор был полезен для вас.
В wordCount
вам не нужен TreeMap
. A TreeMap
заказывает записи по ключам, но вам это совсем не нужно. Цель этого метода - вернуть карту подсчета слов, упорядочение записей не имеет значения. Это не ошибка использования TreeMap
, это просто бессмысленно. A HashMap
было бы лучше.
В printTopWords
вы используете итераторы без типа. Это не очень хорошая практика, и броски внутри цикла уродливы. Этот цикл был бы лучше, используя шаблон итератора:
int wordsCounter = 1;
for (Entry<String, Integer> entry : wordsMap) {
String key = entry.getKey();
int value = entry.getValue();
System.out.println(wordsCounter + ": " + key + " - " + value);
if (++wordsCounter > numberOfWords) {
break;
}
}
Ваша программа не разделяет обязанности хорошо. Вы не должны сортировать и печатать в том же методе. Было бы лучше разделить это на два метода: один для сортировки и другой для печати. В этом случае модульное тестирование будет проще, так как ваши тестовые примеры могут быть основаны на методе сортировки.
Несколько более простой метод сортировки по значениям использовал бы Comparator
с помощью TreeSet
, например:
static class WordCountComparator implements Comparator<String> {
Map<String, Integer> base;
public WordCountComparator(Map<String, Integer> base) {
this.base = base;
}
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return -1;
}
return 1;
}
}
public static List<String> printTopWords(final int numberOfWords, List<String[]> phrases) {
Map<String, Integer> wordCountMap = wordCount(phrases);
Map<String, Integer> wordsSortedByCount = new TreeMap<String, Integer>(new WordCountComparator(wordCountMap));
wordsSortedByCount.putAll(wordCountMap);
// ...
}
Вы можете перебирать записи в wordsSortedByCount
, они сортируются по счету слов.
Имейте в виду, что вы не указали порядок слов, имеющих один и тот же счетчик, поэтому их порядок будет неуказан.
Несколько заметок:
Вы не используете дженерики для своего итератора, если используете
Iterator<Entry<String, Integer>> entries = wordsMap.iterator();
и Entry<String, Integer> entry = entries.next();
вам ничего не нужно придумать.
Вы можете использовать AtomicInteger
вместо Integer
, чтобы избежать использования .put
. Используя AtomicInteger
, вы можете вызвать .incrementAndGet()
on чтобы увеличить значение.