概述
String numberToVoice(int number) 的小测试. eg: 12345 整数转读音成 一万二千三百四十五, 这个题目美团要求是20分钟做完且能跑通, 不会做就不要去面试了.
解决第一阶段, 风风火火完成需求
我开始一看这个题目, 感觉很简单啊, 五位数, 加一个位描述, 对应 万 千 百 十 个位, 建立一个对应的 table就可以了
1 | private static final Map<Integer, String> numberVoiceMap = ImmutableMap.<Integer, String>builder() |
然后每个数, 0~9 同样建立一个table
1 | private static final Map<Integer, String> numberMap = ImmutableMap.<Integer, String>builder() |
接下来, 直接 循环 numberVoiceMap 拿到对应每位的数 和 位描述 即可. 然后啪啪啪就把代码写出来了, 加上测试案例也大概才华了16分钟左右, 而且测试类完美通过, 下面贴下代码
- NumberUtils
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65package com.luozi.api;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Objects;
/**
* Created by luoziyihao on 6/19/17.
*/
public class NumberUtils {
private static final Map<Integer, String> numberVoiceMap = ImmutableMap.<Integer, String>builder()
.putAll(ImmutableMap.of(
10000, "万"
, 1000, "千"
, 100, "百"
, 10, "十"
, 1, ""
))
.build();
private static final Map<Integer, String> numberMap = ImmutableMap.<Integer, String>builder()
.putAll(ImmutableMap.of(
0, "零"
, 1, "一"
, 2, "二"
, 3, "三"
, 4, "四"
))
.putAll(ImmutableMap.of(
5, "五"
, 6, "六"
, 7, "七"
, 8, "八"
, 9, "九"
))
.build();
private static final int high = 100000;
/**
* 整数转读音 只支持到万位
*
* @param num
* @return
*/
public static String numberToVoice(int num) {
if (num >= high) {
throw new IllegalStateException("this method did't support number greater then " + high);
}
StringBuilder builder = new StringBuilder();
for (Map.Entry<Integer, String> entry : numberVoiceMap.entrySet()) {
int count = num / entry.getKey();
if (count >= 1) {
builder.append(numberMap.get(count))
.append(entry.getValue());
}
num = num % entry.getKey();
}
return builder.toString();
}
} - NumberUtilsTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.luozi.api;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Created by luoziyihao on 6/19/17.
*/
public class NumberUtilsTest {
public void numberToVoice() throws Exception {
Assert.assertEquals("一", NumberUtils.numberToVoice(1));
Assert.assertEquals("一十一", NumberUtils.numberToVoice(11));
Assert.assertEquals("一百一十一", NumberUtils.numberToVoice(111));
Assert.assertEquals("一千一百一十一", NumberUtils.numberToVoice(1111));
Assert.assertEquals("一万二千三百四十", NumberUtils.numberToVoice(12340));
Assert.assertEquals("一万二千三百四十五", NumberUtils.numberToVoice(12345));
}
}
解决第二阶段, 加上 0 的处理
上面的代码貌似可以了, 可是突然我想到, 上面的数字咋没有出现 0啊, 万一出现12004这种数怎么办, 代码中没有做这种处理, 肯定有问题. 于是开始改代码, 中间几多艰辛在此不再细表. 反正是又花了二十多分钟, 才改完并测试通过, 好吧, 我承认我没有通过测试. 下面重新贴一下第二版代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94package com.luozi.api;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.Objects;
/**
* Created by luoziyihao on 6/19/17.
*/
public class NumberUtils {
private static final Map<Integer, String> numberVoiceMap = ImmutableMap.<Integer, String>builder()
.putAll(ImmutableMap.of(
10000, "万"
, 1000, "千"
, 100, "百"
, 10, "十"
, 1, ""
))
.build();
private static final Map<Integer, String> numberMap = ImmutableMap.<Integer, String>builder()
.putAll(ImmutableMap.of(
0, "零"
, 1, "一"
, 2, "二"
, 3, "三"
, 4, "四"
))
.putAll(ImmutableMap.of(
5, "五"
, 6, "六"
, 7, "七"
, 8, "八"
, 9, "九"
))
.build();
private static final int high = 100000;
/**
* 整数转读音 只支持到万位
*
* @param num
* @return
*/
public static String numberToVoice(int num) {
if (num >= high) {
throw new IllegalStateException("this method did't support number greater then " + high);
}
StringBuilder builder = new StringBuilder();
for (Map.Entry<Integer, String> entry : numberVoiceMap.entrySet()) {
int count = num / entry.getKey();
if (count >= 1) {
builder.append(numberMap.get(count))
.append(entry.getValue());
} else if (checkIsAppendZero(builder, entry)) {
builder.append(numberMap.get(count));
}
num = num % entry.getKey();
}
CheckAndRemoveSpareZero(builder);
return builder.toString();
}
private static final int zero = 0;
private static void CheckAndRemoveSpareZero(StringBuilder builder) {
int length = builder.length();
while (length > 1 && getLastChar(builder).equals(numberMap.get(zero))) {
length = builder.length();
if (Objects.equals(getLastChar(builder), numberMap.get(zero)) && length > 1) {
builder.deleteCharAt(length - 1);
}
}
}
private static String getLastChar(StringBuilder builder) {
int length = builder.length();
if (length >= 1){
return builder.charAt(length - 1) + "";
} else {
return "";
}
}
private static boolean checkIsAppendZero(StringBuilder builder, Map.Entry<Integer, String> entry) {
int length = builder.length();
return (length > 0 && !Objects.equals(getLastChar(builder), numberMap.get(zero)))
|| entry.getKey() == 1;
}
}- NumberUtilsTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30package com.luozi.api;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Created by luoziyihao on 6/19/17.
*/
public class NumberUtilsTest {
public void numberToVoice() throws Exception {
Assert.assertEquals("一", NumberUtils.numberToVoice(1));
Assert.assertEquals("一十一", NumberUtils.numberToVoice(11));
Assert.assertEquals("一百一十一", NumberUtils.numberToVoice(111));
Assert.assertEquals("一千一百一十一", NumberUtils.numberToVoice(1111));
Assert.assertEquals("一万二千三百四十", NumberUtils.numberToVoice(12340));
Assert.assertEquals("一万二千三百四十五", NumberUtils.numberToVoice(12345));
Assert.assertEquals("一万零四十五", NumberUtils.numberToVoice(10045));
Assert.assertEquals("零", NumberUtils.numberToVoice(0));
Assert.assertEquals("一十", NumberUtils.numberToVoice(10));
Assert.assertEquals("一百", NumberUtils.numberToVoice(100));
Assert.assertEquals("一百零一", NumberUtils.numberToVoice(101));
}
}
解决第三阶段, 支持所有 int 整数
其实在一开始写这个需求的时候, 就默认忽视了一点, 以为这个数处理的最大位数是 5位数, 但实际上人家代码需求里面写的请清楚楚明明白白的是 整数转读音, java里面的整数是 4个字节的, 打开python算一下, 2**31 结果是 2147483648, 10位数,对应二十一亿四千七百八十三万三千六百四十八 ,上面的实现肯定处理不了这个数.
好吧,怎么改呢, 仔细看了看中文描述, 发现除了多了亿和万这两个描述, 每个四位的处理基本上都一样, 那就四位四位地读吧, 延续前面的思路, 定义了新的table
1 | private static final Map<Integer, String> BIGNUMBER_VOICE_MAP = ImmutableMap.<Integer, String>builder() |
前面的方法重构成处理四位数的方法
1 | private static final int ZERO_CHECK_NUMBER = 1000; |
然后在处理四位的 前面又套一层
1 | StringBuilder builder = new StringBuilder(); |
最后加上测试案例, 执行完美通过, 可是这个过程又花了我二十多分钟, 好吧, 我承认我离美团越來越远了, 哈哈
1 | package com.luozi.api; |
下面贴一下实现类的完整代码
1 | package com.luozi.api; |
解决第四阶段, 重构, 代码洁癖狂开始扫垃圾了
不得不承认, 上面某些代码是很丑陋的, 比如将 char 转成String 再比较, zero的描述从map里面取, 完全是多余的逻辑, 整个代码逻辑略显混乱, 方法和参数名都取的不好…