字符串的统计字符-Java流式编程

224 阅读3分钟

字符串的统计字符-MY

题目描述

给定一个字符串str,返回str的统计字符串。例如“aaabbbbcccd”的统计字符串为“a_3_b_4_c_3_d_1”。 输入描述:

输入一行字符串(1<=长度<=10^5)。

输出描述:

输出一行字符串,代表统计字符串。

示例1

输入

offerofferzainaliiiiii

输出

o_1_f_2_e_1_r_1_o_1_f_2_e_1_r_1_z_1_a_1_i_1_n_1_a_1_l_1_i_6

示例2

输入

hhhaaa

输出

h_3_a_3

代码

流式

char[] chars = "aaaabbbbcccd".toCharArray();
        IntStream.range(0, chars.length)
                .boxed()
                .collect(Collectors.toMap( k -> chars[k], v -> 1, (v1, v2) -> v1 + 1 ))
                .entrySet().stream()
                .map(x->"_"+x.getKey()+"_"+x.getValue())
                .reduce((a, b) -> a+b)
                .ifPresent(x-> System.out.println(x.substring(1)));

过程式

char[] chars = "aaaabbbbcccd".toCharArray();
HashMap<Character, Integer> stringIntegerHashMap = new HashMap<>();
for (int i = 0; i < chars.length; i++) {
    if (!stringIntegerHashMap.containsKey(chars[i])){
        stringIntegerHashMap.put(chars[i],1);
    }else {
        stringIntegerHashMap.replace(chars[i],stringIntegerHashMap.get(chars[i])+1);
    }
}
StringBuilder s= new StringBuilder();
Set<Map.Entry<Character, Integer>> entries = stringIntegerHashMap.entrySet();
Iterator<Map.Entry<Character, Integer>> iterator = entries.iterator();
for (int i = 0; i < entries.size(); i++) {
    Map.Entry<Character, Integer> next = iterator.next();
    s.append("_").append(next.getKey()).append("_").append(next.getValue());
}
System.out.println(s.substring(1));

结果

a_4_b_4_c_3_d_1

总结

概述

这道算法题的关键点在于记录char数组中,无重复字符的个数。 大致流程就是指针扫描char数组中的每个字符,判断字符是否出现过,如果有就+1,而对于从来就没有出现的字符默认就是赋值1. 对于如何记录无重复字符个数,无疑使用hashmap是最合适的。 两种写法的思路其实是一样的,流式编程(函数式编程)并不会带来性能上的提升。

流式编程写法中的一些操作

IntStream.range(0, chars.length)

使用IntStream.range()方法生成范围内从一个初始值到最大值的一系列数字。上面代码中初始值是0,最大值是chars.length(要操作的字符数量)

.boxed()

此处方法为装箱,将int转为Integer,因为toMap()方法要生成一个hashmap,而hashmap使用的类型必须是包装类。对于数组的操作int足以,但是不使用boxed()编译器会报错,我觉得应该是类型推导的原因。

.collect(Collectors.toMap( k -> chars[k], v -> 1, (v1, v2) -> v1 + 1 ))

这里的重点是toMap(),它实际上接受了三个参数,第一个是key,第二个是value,第三个是当前key在hashmap里面若有重复让你选取值,v1(已存在key对应的value值)还是v2(当前key的值)。由于三个参数都是函数式接口,写起来十分简洁。

.entrySet().stream()

将hashmap转entrySet,再开启流。这样才可以对hashmap中的值操作。

.map(x->"_"+x.getKey()+"_"+x.getValue())

映射操作,将entrySet中的key和value与字符串拼接。注意,这里的类型已经转换为String了。下面开始操作的数据类型就是String。

.reduce((a, b) -> a+b)

归并操作,会从流里面取值充当参数,a是第一次流的值,b是第二次流的值,此处将他们归并为两个字符串拼接。举个例子,"_a_3"+"_b_2",拼接结果就是这样,直至将entrySet中所有的数据拼接完。

.ifPresent(x-> System.out.println(x.substring(1)));

为什么是ifPresent()?因为reduce()操作完了返回的是Optional类型的值,这里返回的是Optional<String>类型,ifPresent正是Optional的方法,作用是如果存在数据将如何处理。最后使用lambda打印从位置1开始截取的子字符串。

感想

流式编程特点是对数据处理十分方便,并且看着简洁优美,流程一次写完,甚至不需要中间变量,底层已经优化好了,通过高级抽象,我们只需要关心做什么而不是怎么做,但是对于初学者来说阅读性可能不是很好, 而过程式编程对于细节把控十分到位,尽管很啰嗦。 第一次发文章,不知道为啥图片上传不上来。如有错误,恳请指正!😊