如何在Java中将字符映射到数字位置?

13

例如:

  • 输入: ['A', 'Z', 'F', 'D', ...]
  • 输出: [0, 25, 5, 3, ...]

在 C 语言中,只需要将字符减去 'A' 即可,但是在 Java 中好像无法这样做。

7个回答

29

使用 String 对象上的 indexOf 方法。例如,

"ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf('F')

返回 5。


3
如果您不需要范围检查,使用 'F' - 'A' 的速度会更快。 - Thilo
不要忘记考虑错误情况--如果indexOf返回-1,那意味着它给出了一个不在字符串中的字符(在这种情况下,除了大写字母之外的任何字符)。 - Etaoin
Thilo - 没有显著的影响。搜索域的大小被限制为26个字符; 计算机很快。我刚刚在我的桌面电脑上进行了一个快速检查; 对于最坏情况(查找“Z”),我的方法用5毫秒完成,而你的方法用1毫秒完成。(实际上,不管我们搜索哪个字母,这些数字都没有任何差别;对于这个小的搜索域,两种方法都是近似常数时间。)如果这发生了无数次,那么是的,您会想要削减它——但否则,我认为您正在过早地优化。 - Etaoin
您可以使用哈希映射来获得类似的结果,并且可能具有更好的执行时间。您真的需要4毫秒吗?如果是这样,您可能会从其他优化中受益,例如缓存。 - Stefan Kendall
这些函数是否不区分大小写?我需要它返回A-1和a-1,B-2和b-2。 - Mahendran
显示剩余2条评论

16

在Java中,您也可以对char类型进行简单的数学运算:

    System.out.println('A' - 'A');

将输出0。


3
注意,这些必须是字符而不是字符串。单引号很重要。 - Thilo
2
这将会针对域外的字符返回虚假值。C语言版本也是这样。 - Thilo
4
@Stefan - 整个问题(由 OP 提出)都很脆弱。关于“字符的数值位置”的概念充满了对字符集的假设,对什么是有效字符的假设等等。请记住他来自 C 的背景... - Stephen C
2
@Stefan,这是明确定义的... Java使用UTF-16字符,字母'A'...'Z'在ASCII兼容范围内。 - Michael Aaron Safyan
1
@Stefan:如果你添加一个简单的 result = (result < 0 || result > 25) ? -1 : result,这个方法将会返回与你提出的解决方案完全相同的结果。 - Thilo
显示剩余2条评论

6
实际上,其他解决方案的弱点在于它们涉及字符串的创建。
public enum Alphabet {
    A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
}

现在,您可以使用 ordinal 函数来获取此处的偏移量。例如 Alphabet.L.ordinal();

然而,鉴于我认为您正在处理函数,这是一个更有用的定义。

public enum Alphabet {
    A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z;

    public static int getNum(String targ) {
        return valueOf(targ).ordinal();
    }

    public static int getNum(char targ) {
        return valueOf(String.valueOf(targ)).ordinal();
    }    
}

注意:与其他语言不同,你可以像声明类一样在它自己的文件中声明枚举。实际上,正如上面所示,枚举可以包含字段和方法,这些字段是静态创建的,很难破坏。事实上,仅具有本地方法和变量以及一个名为INSTANCE的单个枚举类型的枚举的使用是创建单例的推荐方式,因为即使通过反射也无法破坏它。
如果你没有控制对函数的调用,你可能想考虑在那里添加一个toUppercase()调用
如果您想更动态地创建字母表而不是使用预定义的字母表,则应查看映射。

2
这里有一个在对数时间内运行的不同实现:
import java.util.Arrays;
import java.util.Collections;

public class CharacterIndex {
    private char[] characters = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
    public int index(char character) {
        assert characters != null;
        return Arrays.binarySearch(characters, Character.toUpperCase(character));                
    }
}

单元测试

import org.junit.Before;
import org.junit.Test;

import static junit.framework.Assert.assertEquals;

public class CharacterIndexTest {
    private CharacterIndex characterIndex;
    @Before
    public void createIndex() {
        characterIndex = new CharacterIndex();
    }
    @Test
    public void testIndexOfLetterA() {
        assertEquals(0, characterIndex.index('A'));
        assertEquals(0, characterIndex.index('a'));
    }
    @Test
    public void testNotALetter() {
        assertEquals(-1, characterIndex.index('1'));
    }

}

2
你期望得到的输出只是大写字母相对于字符'A'的偏移量。因此,只需从需要偏移量的字母的Unicode值中减去字符'A'的Unicode值即可。
例如:'B' - 'A' = 1

@Stefan:我只会将上述内容应用于大写字母,而不是任何字符。 - codaddict
3
“本质上错误”这个说法有点过于强硬了。它假设输入来自正确的范围,但这并没有什么问题。 - Thilo
实际上,即使输入无效,也不会发生任何不良反应。您只会得到一个超出范围0..25的数字,这与使用indexOf()解决方案完全相同。 - Thilo
...但它允许其他人轻松地破解该方法。像这样编写的函数不能保证其所期望的不变量。它是易碎的、脆弱的,是一种贫民的解决方案。 - Stefan Kendall

0

您可以使用 java.lang.Character.toUpperCase('a') - 65;


-1
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return alphabet.indexOf( myChar );

2
@Stefan,那是一个可怕的解决方案,因为你正在通过字母进行线性搜索。当然,它是常数时间,因为你有一个固定大小的字母表,但它是不必要的缓慢。 - Michael Aaron Safyan
6
另外,对于给其他人的投票打负分是很讨厌的行为,不要这样做。 - Michael Aaron Safyan
然后使用字符到数字值的映射。相比于调用哈希码,此解决方案需要进行26个字符比较(平均为13个),因此在速度上只能实现轻微的节省。 - Stefan Kendall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接