用Jdk17的风格书写代码

我想通过Java不同版本的编程特性去解决一下开发中会遇到的问题,帮我们快速理解和学习新特性,后续我会不断的收集和补充这些问题,目前比较可惜的是我还没有对不同的jdk版本的单元测试代码进行banchmark……

Q1:在给定的字符串中取出出现次数Top3的字母,但是可能会出现并列情况,所以我们的结果应该是[k=[char1,chat2,……],……]

  • A:jdk8之前
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
@Test
public void testCharTopK() {
String python = " Python is powerful... and fast;\n" +
" plays well with others;\n" +
" runs everywhere;\n" +
" is friendly & easy to learn;\n" +
" is Open.";

// key:char value:char 出现次数
Map<Character, Long> key2count = new HashMap<>();
char[] pythonChars = python.toLowerCase().toCharArray();
for (Character c : pythonChars) {
if (!Character.isAlphabetic(c)) {
continue;
}
Long count = key2count.getOrDefault(c, 0L);
key2count.put(c, count + 1);
}

// key:count value:chars ,像直方图
TreeMap<Long, List<Character>> count2chars = new TreeMap<>((o1, o2) -> Math.toIntExact(o2 - o1));

for (Map.Entry<Character, Long> entry : key2count.entrySet()) {
List<Character> chars = count2chars.getOrDefault(entry.getValue(), Lists.newArrayList());
chars.add(entry.getKey());
count2chars.put(entry.getValue(), chars);
}

log.info("sorted result {}", count2chars);
}
  • A:jdk8之后,jdk16之前
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
@Test
public void testCharTopK() {
var python = """
Python is powerful... and fast;
plays well with others;
runs everywhere;
is friendly & easy to learn;
is Open.
""";

// key:char value:char 出现次数
Map<Character, Long> key2count = python.chars()
.filter(Character::isAlphabetic).mapToObj(i -> (char) i)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

// key:count value:chars ,像直方图
Map<Long, List<Character>> count2char = key2count.entrySet().stream().
collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())
)
);
// 倒排
List<Map.Entry<Long, List<Character>>> topK = count2char.entrySet().stream()
.sorted(Map.Entry.comparingByKey(Comparator.reverseOrder())).toList();

log.info("sorted result {}", topK);
}
  • A:jdk16之后,更适用于可读性更好的业务代码
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
@Test
public void testCharTopK() {
var python = """
Python is powerful... and fast;
plays well with others;
runs everywhere;
is friendly & easy to learn;
is Open.
""";
record Letter(int codePoint) {
Letter(int codePoint) {
this.codePoint = Character.toLowerCase(codePoint);
}
}
record LetterCount(long count) implements Comparable<LetterCount> {
@Override
public int compareTo(LetterCount o) {
return Long.compare(this.count, o.count);
}
}

record LetterByCount(Letter letter, LetterCount count) {
LetterByCount(Map.Entry<Letter, LetterCount> entry) {
this(entry.getKey(), entry.getValue());
}
}

record LettersByCount(LetterCount count, List<Letter> letters) {
LettersByCount(Map.Entry<LetterCount, List<Letter>> entry) {
this(entry.getKey(), entry.getValue());
}

public static Comparator<? super LettersByCount> comparingByCount() {
return Comparator.comparing(LettersByCount::count);
}
}


// key:char value:char 出现次数
Map<Letter, LetterCount> letter2count = python.chars()
.filter(Character::isAlphabetic).mapToObj(Letter::new)
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.collectingAndThen(Collectors.counting(), LetterCount::new)
)
);

// key:count value:chars ,像直方图
Map<LetterCount, List<Letter>> count2letter = letter2count.entrySet().stream()
.map(LetterByCount::new)
.collect(Collectors.groupingBy(LetterByCount::count, Collectors.mapping(LetterByCount::letter, Collectors.toList())));

// 倒排
List<LettersByCount> topK = count2letter.entrySet().stream()
.map(LettersByCount::new)
.sorted(LettersByCount.comparingByCount().reversed())
.toList();
log.info("sorted result {}", topK);
}

Q2:猜数字游戏:一位玩家想一个单词,另一位通过单词长度尝试猜该玩家所想的单词,如果同位置上的字母相同,则在猜测的单词上该字母大写返回,如果同位置上没有,但是其它位置有则不变(只匹配一次),如果同位置不匹配且它位置未出现则用.替换.

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
@Test
public void testGuess() {
record Wordless(String hidden) {
public String guess(String guess) {
StringBuilder result = new StringBuilder();
boolean[] used = new boolean[hidden.length()];
for (int i = 0; i < guess.length(); i++) {
if (guess.codePointAt(i) == this.hidden.codePointAt(i)) {
result.append(Character.toString(guess.codePointAt(i)).toUpperCase());
used[i] = true;
} else {
boolean found = false;
for (int j = 0; j < hidden.length(); j++) {
if (used[j]) {
continue;
}
if (i != j
&& guess.codePointAt(j) != this.hidden.codePointAt(j)
&& guess.codePointAt(i) == this.hidden.codePointAt(j)) {
result.append(Character.toString(guess.codePointAt(i)));
used[j] = true;
found = true;
break;
}
}
if (!found) {
result.append(".");
}
}
}
return result.toString();
}
}
Wordless wordless = new Wordless("aaaab");
Assertions.assertEquals(wordless.guess("ccccc"), ".....");
Assertions.assertEquals(wordless.guess("ccacc"), "..A..");
Assertions.assertEquals(wordless.guess("ccacb"), "..A.B");
Assertions.assertEquals(wordless.guess("bbacc"), "b.A..");
}
  • 封装重构之后,看似变长了,但是可读性更高了
    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
    public record Wordless(String hiddenAsStr) {
    public String guess(String guessAsStr) {
    Guess guess = new Guess(guessAsStr);
    Hidden hidden = new Hidden(hiddenAsStr);
    return IntStream.range(0, guess.length()).mapToObj(i -> {
    if (guess.hasALetterAtPosition(i).thatIsWellPlacedIn(hidden)) {
    return Character.toString(guess.codePointAt(i)).toUpperCase();
    } else if (guess.hasALetterAtPosition(i).thatIsNotWellPlacedIn(hidden)) {
    return Character.toString(guess.codePointAt(i));
    } else {
    return ".";
    }
    }).collect(Collectors.joining());
    }
    }

    record Hidden(String hidden, boolean[] used) {
    public Hidden(String hidden) {
    this(hidden, new boolean[hidden.length()]);
    }

    public int codePointAt(int i) {
    return this.hidden.codePointAt(i);
    }

    public int length() {
    return this.hidden.length();
    }

    public boolean match(GuessWithIndex guessWithIndex, int guessIndex, int hiddenIndex) {
    if (used[hiddenIndex]) {
    return false;
    }
    boolean match = guessWithIndex.guess().codePointAt(guessIndex) == hidden.codePointAt(hiddenIndex);
    if (match) {
    used[hiddenIndex] = true;
    }
    return match;
    }
    }

    record Guess(String guess) {

    public int length() {
    return this.guess.length();
    }

    public GuessWithIndex hasALetterAtPosition(int i) {
    return new GuessWithIndex(this, i);
    }

    public int codePointAt(int i) {
    return this.guess.codePointAt(i);
    }
    }

    record GuessWithIndex(Guess guess, int index) {
    public boolean thatIsWellPlacedIn(Hidden hidden) {
    return hidden.match(this, index, index);
    }

    public boolean thatIsNotWellPlacedIn(Hidden hidden) {
    return IntStream.range(0, hidden.length())
    .filter(i -> index != i)
    .filter(i -> guess.codePointAt(i) != hidden.codePointAt(i))
    .anyMatch(i -> hidden.match(this, index, i));
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    public void testGuess() {
    Wordless wordless = new Wordless("aaaab");
    Assertions.assertEquals(wordless.guess("ccccc"), ".....");
    Assertions.assertEquals(wordless.guess("ccacc"), "..A..");
    Assertions.assertEquals(wordless.guess("ccacb"), "..A.B");
    Assertions.assertEquals(wordless.guess("bbacc"), "b.A..");
    }
  • 继续重构,引入密闭类和switch case表达式
    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
    public record Wordless(String hiddenAsStr) {
    public String guess(String guessAsStr) {
    Guess guess = new Guess(guessAsStr);
    Hidden hidden = new Hidden(hiddenAsStr);
    return IntStream.range(0, guess.length()).mapToObj(index -> switch (guess.checkCharterAtPosition(index).with(hidden)) {
    case WELL_PLACED letter -> Character.toString(letter.codePoint()).toUpperCase();
    case NOT_WELL_PLACED letter -> Character.toString(letter.codePoint());
    case ABSEND letter -> ".";
    }).collect(Collectors.joining());
    }
    }

    record Hidden(String hidden, boolean[] used) {
    public Hidden(String hidden) {
    this(hidden, new boolean[hidden.length()]);
    }

    public int codePointAt(int i) {
    return this.hidden.codePointAt(i);
    }

    public int length() {
    return this.hidden.length();
    }

    public boolean match(GuessWithIndex guessWithIndex, int guessIndex, int hiddenIndex) {
    if (used[hiddenIndex]) {
    return false;
    }
    boolean match = guessWithIndex.guess().codePointAt(guessIndex) == hidden.codePointAt(hiddenIndex);
    if (match) {
    used[hiddenIndex] = match;
    }
    return match;
    }
    }

    record Guess(String guess) {

    public int length() {
    return this.guess.length();
    }

    public GuessWithIndex hasALetterAtPosition(int i) {
    return new GuessWithIndex(this, i);
    }

    public int codePointAt(int i) {
    return this.guess.codePointAt(i);
    }

    public GuessWithIndex checkCharterAtPosition(int index) {
    return new GuessWithIndex(this,index);
    }
    }

    record GuessWithIndex(Guess guess, int index) {
    public boolean thatIsWellPlacedIn(Hidden hidden) {
    return hidden.match(this, index, index);
    }

    public boolean thatIsNotWellPlacedIn(Hidden hidden) {
    return IntStream.range(0, hidden.length()).filter(i -> index != i)
    .filter(i -> guess.codePointAt(i) != hidden.codePointAt(i))
    .anyMatch(i -> hidden.match(this, index, i));
    }

    public Letter with(Hidden hidden) {
    if (thatIsWellPlacedIn(hidden)) {
    return new WELL_PLACED(this.guess.codePointAt(this.index));
    } else if (thatIsNotWellPlacedIn(hidden)) {
    return new NOT_WELL_PLACED(this.guess.codePointAt(this.index));
    } else {
    return new ABSEND();
    }
    }
    }

    sealed interface Letter permits WELL_PLACED, NOT_WELL_PLACED, ABSEND {
    }

    record WELL_PLACED(int codePoint) implements Letter {
    }

    record NOT_WELL_PLACED(int codePoint) implements Letter {
    }

    record ABSEND() implements Letter {
    }
    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    public void testGuess() {
    Wordless wordless = new Wordless("aaaab");
    Assertions.assertEquals(wordless.guess("ccccc"), ".....");
    Assertions.assertEquals(wordless.guess("ccacc"), "..A..");
    Assertions.assertEquals(wordless.guess("ccacb"), "..A.B");
    Assertions.assertEquals(wordless.guess("bbacc"), "b.A..");
    }

用Jdk17的风格书写代码
https://vegetablest.github.io/2023/08/12/用jdk17的风格书写代码/
作者
af su
发布于
2023年8月12日
许可协议