Страницы

Поиск по вопросам

суббота, 21 декабря 2019 г.

Подсчет количества вхождений букв в строку с помощью потока (Stream)

#java #java_stream #java_8


Есть определенная строка(например "Some example"). Нужно определить количество вхождений
каждой буквы в строке с помощью потока Stream и при этом без использования циклов,
if и прочего(лишь рекурсия, потоки Stream и его методы вроде map, reduce, filter). 

Я сделал из строки массив символов, который потом превратил в список c помощью рекурсивного
метода toCharList. 

String text = "Some example".toLowerCase.replaceAll("\\s", "")
List textInChar = toCharList(text.toCharArray(), 0);

static public List toCharList(char[] textChars, int i) {

    if(textChars.length == i) return new ArrayList<>();

    List ret =  new ArrayList<>();
    ret.add(textChars[i]);
    ret.addAll(toCharList(textChars, i + 1));


    return ret;
}


Затем из этого списка создал хэш-мапу c помощью перегруженного метода toCharMap.
Таким образом получил я список всех не повторяющихся букв, которые есть в строке.

Map  countChar = toCharMap(textInChar);

public static Map toCharMap(List l) {
    return toCharMap(l.iterator());
}


public static Map toCharMap(Iterator it) {
    if (!it.hasNext()) return new HashMap<>();

    Map ret = new HashMap<>();
    ret.put((Character)it.next(), 0);
    ret.putAll(toCharMap(it));

    return ret;
}


Но как теперь их посчитать в потоке, например с помощью filter и reduce?
    


Ответы

Ответ 1



1) Из строки массив символов лучше получать так: String text = ("Some example").toLowerCase().replaceAll("\\s", ""); List textInChar = Chars.asList(text.toCharArray()); или: textInChar = text.chars().mapToObj(e->(char)e).collect(Collectors.toList()); 2) Xэш-мапу не повторяющихся символов лучше получить тогда уж так: Map countChar = textInChar.stream().collect(HashMap::new, (m, c) -> { m.put(c, 1); m.put(c, 1); }, HashMap::putAll); А всю задачу я бы решил так: textInChar = text.chars().mapToObj(e->(char)e).collect(Collectors.toList()); Map countChar = textInChar.stream().collect(HashMap::new, (m, c) -> { if(m.containsKey(c)) m.put(c, m.get(c) + 1); else m.put(c, 1); }, HashMap::putAll); Проверим: countChar.forEach( (k, v) -> LOG.debug(k + " -> " + v)); Выведет: p -> 1 a -> 1 s -> 1 e -> 3 x -> 1 l -> 1 m -> 2 o -> 1 UPD: if(m.containsKey(c)) m.put(c, m.get(c) + 1); else m.put(c, 1); Можно сократить до: m.put(c, m.containsKey(c) ? (m.get(c) + 1) : 1);

Ответ 2



Для группировки элементов Stream можно воспользоваться коллектором Collectors.groupingBy. Кроме варианта с одним агрументом (функцией-классификатором) у него есть вариант, принимающий еще один коллектор, c помощью которого будет выполнена редукция групп к одному значению: // Character::valueOf принимает char, а поток у нас из int String output = "Some example".chars().mapToObj( ch -> new Character( (char)ch ) ) // сгруппировать по символам, внутри группы подсчитать количество .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) ) // группировка возвращает Map с количеством разных символов .entrySet().stream() // если надо выбрать только встречающиеся 1 раз //.filter( entry -> entry.getValue() == 1 ) // раз forEach плохо :) //.forEach( entry -> System.out.printf( "'%s' -> %d%n", entry.getKey(), entry.getValue() ) ); .map( entry -> String.format( "'%s' -> %d%n", entry.getKey(), entry.getValue() ) ) .collect( Collectors.joining() ); System.out.println( output );

Ответ 3



String string = "Some example"; Map result = IntStream .range(0, string.length()) // .parallel() // can be parallel for very big strings .mapToObj(string::charAt) .collect( toMap( Function.identity(), c -> new AtomicInteger(1), (count1, count2) -> { count1.addAndGet(count2.getAndSet(0)); return count1; }) ); для сравнения императивная реализация: String string = "Some example"; Map map = new HashMap<>(); for (int i = 0; i < string.length(); i++) { map .computeIfAbsent(string.charAt(i), character -> new AtomicInteger()) .incrementAndGet(); } которая гораздо производительнее при любых раскладах

Комментариев нет:

Отправить комментарий