update
All checks were successful
Deploy / deploy (push) Successful in 14s

This commit is contained in:
me
2026-04-14 15:26:42 +03:00
parent bec44ad8af
commit 38158e5319
31 changed files with 3275 additions and 341 deletions

View File

@@ -0,0 +1,38 @@
// SumHex.java
public class SumHex {
public static void main(String[] args) {
int sum = 0;
for (String argument : args) {
StringBuilder number = new StringBuilder();
for (char c : argument.toCharArray()) {
if (!Character.isWhitespace(c)) {
number.append(c);
} else {
sum += parseNumber(number);
number = new StringBuilder();
}
}
sum += parseNumber(number);
}
System.out.println(sum);
}
static long parseNumber(StringBuilder number) {
if (!number.isEmpty()) {
String numberString = number.toString();
if (
numberString.startsWith("0x") || numberString.startsWith("0X")
) {
return Long.parseLong(numberString.substring(2), 16);
} else {
return Integer.parseInt(numberString);
}
} else {
return 0;
}
}
}

View File

@@ -0,0 +1,226 @@
---
title: SumHex
weight: 6
---
# Task
- The input is in decimal and hexadecimal numbers
- Hexademical numbers has the prefix `0x`
- The input is case insensitive
- Class should be named `SumHex`
## Solution
This modification doesn't seem too complicated either.
Let's modify our initial `Sum.java` and apply all our improvements from a previous modification. (A good exercise would be to try doing that yourself and then comeback to check)
```java
// SumHex.java
public class SumHex {
public static void main(String[] args) {
int sum = 0;
for (String argument : args) {
StringBuilder number = new StringBuilder();
for (char c : argument.toCharArray()) {
if (!Character.isWhitespace(c)) {
number.append(c);
} else {
sum += parseNumber(number);
number = new StringBuilder();
}
}
sum += parseNumber(number);
}
System.out.println(sum);
}
static int parseNumber(StringBuilder number) {
return number.isEmpty() ? 0 : Integer.parseInt(number.toString());
}
}
```
> [!NOTE]
> The importance of these changes, is that in all further modifications we will only be changing our parseNumber method and leaving `main` method almost unchanged. The only modifications to `main` would be changing the data type of `sum`.
Now `parseNumber` thinks that every number is decimal, but we don't know what to do with hexadecimal numbers. Let's try that
```bash
$ java SumHex 0x1 0x2 0x3
Exception in thread "main" java.lang.NumberFormatException: For input string: "0x1"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:662)
at java.base/java.lang.Integer.parseInt(Integer.java:778)
at SumHex.parseNumber(SumHex.java:25)
at SumHex.main(SumHex.java:18)
```
So we need to split decimal and hexadecimal processing inside `parseNumber`. Our code would look something like this.
```java
static int parseNumber(StringBuilder number) {
if (!number.isEmpty()) {
if (/* number is hexadecimal */) {
return /* parse hexadecimal */;
} else { // if the number if decimal
return Integer.parseInt(number.toString());
}
}
return 0;
}
```
Here we have 2 major questions:
- How to check if the number is hexadecimal?
- How to parse the hexadecimal number?
We can answer the 1st question, because we know that hexadecimal number starts with `"0x"`. And we know that the input is not case sensitive, so `"0X"` is also a valid option.
So we can change our *pseude-code* like this
```java
static int parseNumber(StringBuilder number) {
if (!number.isEmpty()) {
if (/* number starts with "0x" or "0X" */) {
return /* parse hexadecimal */;
} else { // if the number is decimal
return Integer.parseInt(number.toString());
}
}
return 0;
}
```
But how to check if the string starts with some prefix? There is a [`String.startsWith(String prefix)`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#startsWith-java.lang.String-) that returns `true` if the string starts with `prefix` and `false` otherwise.
So we can change our code using this information.
```java
static int parseNumber(StringBuilder number) {
if (!number.isEmpty()) {
String numberString = number.toString();
if (
numberString.startsWith("0x") ||
numberString.startsWith("0X")
) {
return /* parse hexadecimal */;
} else { // if the number is decimal
return Integer.parseInt(number.toString());
}
}
return 0;
}
```
> [!NOTE]
> `||` here is a logical ***or***.
We still have one problem. How to parse the hexadecimal number?
Using [`Integer.parseInt()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#parseInt-java.lang.String-int-) method. It turnes out, that it has the second argument `radix` where we can specify the base of the numerical system (in our case it's `16`).
So our code will look like this.
```java
static int parseNumber(StringBuilder number) {
if (number.isEmpty()) {
String numberString = number.toString();
if (
numberString.startsWith("0x") ||
numberString.startsWith("0X")
) {
return Integer.parseInt(numberString.substring(2), 16);
} else {
return Integer.parseInt(numberString);
}
}
return 0;
}
```
> [!NOTE]
> Here we used [`String.substring(int beginIndex)`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#substring-int-) method to cut off first two symbols (`"0x"`/`"0X"`) in order for `parseInt` to parse number correctly and not give us exceptions. In our case `beginIndex = 2` because it's the first index of the substring that we want to get. Remember that ***indexing starts with 0*** (not 1).
> [!IMPORTANT]
> It's important to know that we can't store all hexadecimal numbers in `int` data type. Let's look at this example
> ```bash
> $ java SumHex 0x80000000
> Exception in thread "main" java.lang.NumberFormatException: For input string: "80000000" under radix 16
> at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
> at java.base/java.lang.Integer.parseInt(Integer.java:662)
> at SumHex.parseNumber(SumHex.java:30)
> at SumHex.main(SumHex.java:18)
> ```
> That's why we will need to use `long` data type for storing hexademical values.
Here's the updated code
```java
static long parseNumber(StringBuilder number) { // int -> long
if (!number.isEmpty()) {
String numberString = number.toString();
if (numberString.startsWith("0x") || numberString.startsWith("0X")) {
// Integer.parseInt -> Long.parseLong
return Long.parseLong(numberString.substring(2), 16);
} else {
return Integer.parseInt(numberString);
}
} else {
return 0;
}
}
```
Let's present the full solution
```java
// SumHex.java
public class SumHex {
public static void main(String[] args) {
int sum = 0;
for (String argument : args) {
StringBuilder number = new StringBuilder();
for (char c : argument.toCharArray()) {
if (!Character.isWhitespace(c)) {
number.append(c);
} else {
sum += parseNumber(number);
number = new StringBuilder();
}
}
sum += parseNumber(number);
}
System.out.println(sum);
}
static long parseNumber(StringBuilder number) {
if (!number.isEmpty()) {
String numberString = number.toString();
if (numberString.startsWith("0x") || numberString.startsWith("0X")) {
return Long.parseLong(numberString.substring(2), 16);
} else {
return Integer.parseInt(numberString);
}
} else {
return 0;
}
}
}
```
> [!IMPORTANT]
> Notice, that we didn't change `main` method at all, because we have a separate function handling parsing. That's a good sign because our `main` method doesn't know what number parsing algorithm is used and therefore not dependent on it.