Converting Between Roman and Arabic Numerals in Java – 在Java中转换罗马数字和阿拉伯数字

最后修改: 2018年 6月 30日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

The ancient Romans developed their own numeric system called Roman numerals. The system uses letters with different values to represent numbers. Roman numerals are still used today in some minor applications.

古罗马人开发了他们自己的数字系统,称为罗马数字。该系统使用具有不同数值的字母来表示数字。罗马数字今天仍在一些小的应用中被使用。

In this tutorial, we’ll implement simple converters that will transform numbers from one system to the other.

在本教程中,我们将实现简单的转换器,将数字从一个系统转换到另一个系统。

2. Roman Numerals

2.罗马数字

In the Roman system, we have 7 symbols that represent numbers:

在罗马系统中,我们有7个代表数字的符号

  • I represents 1
  • V represents 5
  • X represents 10
  • L represents 50
  • C represents 100
  • D represents 500
  • M represents 1000

Originally, people used to represent a 4 with IIII or 40 with XXXX. This can be quite uncomfortable to read. It’s also easy to mistake four symbols next to each other for three symbols.

最初,人们习惯用IIII表示4,用XXXX表示40。这样读起来会很不舒服。也很容易把相邻的四个符号误认为三个符号。

Roman numerals use subtractive notation to avoid such mistakes. Instead of saying four times one (IIII), one can say that it’s one less than five (IV).

罗马数字使用减法符号以避免此类错误。与其说是四乘以一(IIII),不如说是比五少一(IV)。

How’s it important from our perspective? It’s important because instead of simply adding numbers symbol by symbol, we might need to check the next symbol to determine if the number should be added or subtracted.

从我们的角度来看,这有什么重要的?它的重要性在于,我们可能需要检查下一个符号,以确定该数字是否应该被添加或减去,而不是简单地逐个符号添加数字。

3. Model

3.模型

Let’s define an enum to represent the Roman Numerals:

让我们定义一个枚举来表示罗马数字。

enum RomanNumeral {
    I(1), IV(4), V(5), IX(9), X(10), 
    XL(40), L(50), XC(90), C(100), 
    CD(400), D(500), CM(900), M(1000);

    private int value;

    RomanNumeral(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
    
    public static List<RomanNumeral> getReverseSortedValues() {
        return Arrays.stream(values())
          .sorted(Comparator.comparing((RomanNumeral e) -> e.value).reversed())
          .collect(Collectors.toList());
    }
}

Notice that we’ve defined additional symbols to help out with subtractive notation. We have also defined an additional method named getReverseSortedValues().

请注意,我们定义了额外的符号来帮助处理减法记号。我们还定义了一个名为getReverseSortedValues()的额外方法。

This method will allow us to explicitly retrieve the defined Roman numerals in descending value order.

这个方法将允许我们明确地按降值顺序检索定义的罗马数字。

4. Roman to Arabic

4.罗马语到阿拉伯语

Roman numerals can only represent integers between 1 to 4000. We can use the following algorithm to convert a Roman numeral to an Arabic number (iterating through symbols in reverse order from M to I):

罗马数字只能代表1到4000之间的整数。我们可以使用以下算法将罗马数字转换为阿拉伯数字(按照从MI的相反顺序迭加符号)。

LET numeral be the input String representing an Roman Numeral
LET symbol be initialy set to RomanNumeral.values()[0]
WHILE numeral.length > 0:
    IF numeral starts with symbol's name:
        add symbol's value to the result
        remove the symbol's name from the numeral's beginning
    ELSE:
        set symbol to the next symbol

4.1. Implementation

4.1.实施

Next, we can implement the algorithm in Java:

接下来,我们可以在Java中实现该算法。

public static int romanToArabic(String input) {
    String romanNumeral = input.toUpperCase();
    int result = 0;
        
    List<RomanNumeral> romanNumerals = RomanNumeral.getReverseSortedValues();

    int i = 0;

    while ((romanNumeral.length() > 0) && (i < romanNumerals.size())) {
        RomanNumeral symbol = romanNumerals.get(i);
        if (romanNumeral.startsWith(symbol.name())) {
            result += symbol.getValue();
            romanNumeral = romanNumeral.substring(symbol.name().length());
        } else {
            i++;
        }
    }

    if (romanNumeral.length() > 0) {
        throw new IllegalArgumentException(input + " cannot be converted to a Roman Numeral");
    }

    return result;
}

4.2. Test

4.2.测试

Finally, we can test the implementation:

最后,我们可以测试实现。

@Test
public void given2018Roman_WhenConvertingToArabic_ThenReturn2018() {
    String roman2018 = "MMXVIII";

    int result = RomanArabicConverter.romanToArabic(roman2018);

    assertThat(result).isEqualTo(2018);
}

5. Arabic to Roman

5.从阿拉伯语到罗马语

We can use the following algorithm to convert from Arabic to Roman numerals (iterating through symbols in reverse order from M to I):

我们可以使用以下算法将阿拉伯数字转换为罗马数字(按照从MI的相反顺序迭加符号)。

LET number be an integer between 1 and 4000
LET symbol be RomanNumeral.values()[0]
LET result be an empty String
WHILE number > 0:
    IF symbol's value <= number:
        append the result with the symbol's name
        subtract symbol's value from number
    ELSE:
        pick the next symbol

5.1. Implementation

5.1.实施

Next, we can now implement the algorithm:

接下来,我们就可以实现这个算法了。

public static String arabicToRoman(int number) {
    if ((number <= 0) || (number > 4000)) {
        throw new IllegalArgumentException(number + " is not in range (0,4000]");
    }

    List<RomanNumeral> romanNumerals = RomanNumeral.getReverseSortedValues();

    int i = 0;
    StringBuilder sb = new StringBuilder();

    while ((number > 0) && (i < romanNumerals.size())) {
        RomanNumeral currentSymbol = romanNumerals.get(i);
        if (currentSymbol.getValue() <= number) {
            sb.append(currentSymbol.name());
            number -= currentSymbol.getValue();
        } else {
            i++;
        }
    }

    return sb.toString();
}

5.2. Test

5.2.测试

Finally, we can test the implementation:

最后,我们可以测试实现。

@Test
public void given1999Arabic_WhenConvertingToRoman_ThenReturnMCMXCIX() {
    int arabic1999 = 1999;

    String result = RomanArabicConverter.arabicToRoman(arabic1999);

    assertThat(result).isEqualTo("MCMXCIX");
}

6. Conclusion

6.结论

In this quick article, we’ve shown how to convert between Roman and Arabic numerals.

在这篇快速文章中,我们展示了如何在罗马数字和阿拉伯数字之间转换。

We have used an enum to represent the set of Roman numerals and we have created a utility class to perform the conversions.

我们使用了一个enum来表示罗马数字的集合,我们创建了一个实用类来执行转换。

The complete implementation and all tests can be found over on GitHub.

完整的实现和所有的测试可以在GitHub上找到over