Печать наиболее часто используемых слов из фраз. Самый эффективный способ заполнения вектора целых чисел без дубликатов. Обратная обратная связь, запрашиваемая при простых путях. Проверка на 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;
    }
}
0 голосов | спросил ComputernerdOverly ExcessiveRohan JamesSamSebastia 10 FebruaryEurope/MoscowbMon, 10 Feb 2014 17:25:12 +0400000000pmMon, 10 Feb 2014 17:25:12 +040014 2014, 17:25:12

3 ответа


12

Как уже отмечали самые очевидные улучшения, я хотел бы поговорить о Java 8. Возможно, вы еще не можете использовать его, но я бы рекомендовал изучить его, и этот ответ должен быть полезен для тех, кто это читает.

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

У вас есть метод, в котором вы принимаете аргумент List<String[]>, и вы хотите вернуть верхние вхождения x, то есть все вы хотите, чтобы это означало:

  1. Поместите все String s вместе в некоторой структуре.
  2. Следите за тем, сколько мест они имеют.
  3. Сначала сортируйте список, чтобы иметь слова с наивысшим количеством вступлений.
  4. Верните верхние вхождения x.
  5. Затем решите, что вы хотите с ним сделать.

Выпуски 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

Объяснение:

  1. У вас есть ваши фразы List<String[]>, вы хотите просто иметь объект, который содержит ваш String s. Здесь Stream<String> является подходящим объектом, потому что вам нужно иметь view на вашем phrases, нет смысла фактически хранить новые данные. Вы делаете это, вызывая phrases.stream().flatMap(Arrays::stream).
    1. Сначала вы превратите свой List<String[]> в Stream<String[]>.
    2. Затем вы используете ссылку на метод, которая описывает lambda stringArray -> Arrays.stream(stringArray) для получения Stream<String>
    3. Затем с помощью flatMap вы добавляете все элементы результирующего Stream<String> обратно в исходный Stream<String>.
  2. Затем вы начинаете с метода getTopWords, который возвращает Stream<Map.Entry<String, Long>>, чтобы предложить гибкость, чтобы сделать то, что вы хотите с результатами, они не сохраняются еще в точке, где он возвращается.
  3. Сначала я добавил некоторые проверки ошибок.
  4. Затем я получаю код Comparator<Map.Entry<String, Long>>, который будет сравнивать то, что входит в число lower . Это делается с помощью Comparator.comparingLong по значению записи, которое получается с помощью ссылки на метод Map.Entry::getValue
  5. Затем я запускаю цепочку операций на входе Stream<String>:
    1. Сначала создайте результаты по их идентификатору, который обычно создает Map<String, List<String>>.
    2. Фокус в том, что я также использовал downstream Collector, который подсчитывает количество раз строка, поэтому она называется Collectors.counting().
    3. В этот момент у меня есть Map<String, Long>, обозначающий слово и количество вхождений. Он использует Long, потому что это то, что Collectors.counting() возвращает.
    4. Затем я получаю код Set<Map.Entry<String, Long>> и преобразую его в поток.
    5. Затем я вызываю sorted() на Stream<Map.Entry<String, Long>> с помощью обратного компаратора. Это делается здесь, потому что интерференция типа не достаточно сильная, чтобы использовать Comparator.comparingLong(Map.Entry::getValue).reversed().
    6. Затем I limit() поток сверху xэлементы.
  6. Теперь у нас есть Stream<Map.Entry<String, Long>>, и здесь мы решили собрать его в List<Map.Entry<String, Long>>
  7. Здесь мы продолжим с вашей старой логикой подключения счетчика.

Несколько замечаний, которые стоит отметить:

  1. Явный comparator.reversed() является уродливым, но необходимо, чтобы не приводить к типу casting, что еще более уродливо, это ограничение тока. Это может быть только проблемой в среде IDE, и компилятор javac может действительно скомпилировать его, хотя.
  2. Использование Map.Entry<String, Long> довольно раздутое, но наш самый разумный вариант, помимо создания Pair и используя Pair<String, Long>. Это, надеюсь, будет проще, если Java 9 включает в себя кортежи (которые логически включают пары) в качестве более или менее первоклассных граждан.
  3. В целом метод getTopWords мы в конечном итоге сохраняем все записи в памяти один раз с помощью Map<String, Long>, я уверен, что есть способы обойти это, но не стоит усилий здесь, только оптимизируйте это, если это станет настоящим узким местом.
  4. Я надеялся использовать метод Stream.forEach() при обработке результатов, однако это невозможно с требованием, которое вы хотите иметь a счетчик . Опять-таки, больше возможностей открывается в Java 9, когда мы надеемся, что BiStream s и кортежи.

Я надеюсь, что этот обзор был полезен для вас.

ответил skiwi 23 AMpWed, 23 Apr 2014 01:00:58 +040000Wednesday 2014, 01:00:58
10

В 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, они сортируются по счету слов.

Имейте в виду, что вы не указали порядок слов, имеющих один и тот же счетчик, поэтому их порядок будет неуказан.

ответил janos 22 PMpTue, 22 Apr 2014 20:20:39 +040020Tuesday 2014, 20:20:39
7

Несколько заметок:

Вы не используете дженерики для своего итератора, если используете

Iterator<Entry<String, Integer>> entries = wordsMap.iterator();

и Entry<String, Integer> entry = entries.next();

вам ничего не нужно придумать.


Вы можете использовать AtomicInteger вместо Integer, чтобы избежать использования .put. Используя AtomicInteger, вы можете вызвать .incrementAndGet() on чтобы увеличить значение.

ответил Simon Forsberg 22 PMpTue, 22 Apr 2014 18:52:11 +040052Tuesday 2014, 18:52:11

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132