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

@@ -3,25 +3,25 @@
public class SumDouble {
public static void main(String[] args) {
double sum = 0; // int -> double
double sum = 0;
for (String argument : args) {
StringBuilder number = new StringBuilder();
for (char c : argument.toCharArray()) {
if (!Character.isWhitespace(c)) {
number.append(c);
} else {
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt -> Double.parseDouble())
}
sum += parseNumber(number); // method call
number = new StringBuilder();
}
}
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt() -> Double.parseDouble()
}
sum += parseNumber(number); // method call
}
System.out.println(sum);
}
static double parseNumber(StringBuilder number) {
return number.isEmpty() ? 0.0 : Double.parseDouble(number.toString());
}
}

View File

@@ -0,0 +1,131 @@
---
title: SumDouble
weight: 6
---
Below are some modifications to the code for deeper understanding of basic language constructions primarily data types. It is always advised to try completing them by yourself before reading the solution. Modifications progress in difficulty from easy to hard.
# Task
- Input data is 64-bytes floating point numbers.
- Class should be named `SumDouble`.
## Solution
This modification is fairly easy. All we need to do is to replace all integers with floating point numbers.
> [!NOTE]
> Here we can see all of the benefits of using `Character.isWhitespace()`, because if we had had for example a set of digits and check if the character is in that set we would need to add `.` (point) to that set to count floating point numbers as well.
So here's the updated code
```java
// SumDouble.java
public class SumDouble {
public static void main(String[] args) {
double sum = 0; // int -> double
for (String argument : args) {
StringBuilder number = new StringBuilder();
for (char c : argument.toCharArray()) {
if (!Character.isWhitespace(c)) {
number.append(c);
} else {
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt -> Double.parseDouble())
}
number = new StringBuilder();
}
}
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt() -> Double.parseDouble()
}
}
System.out.println(sum);
}
}
```
This code will work fine and pass all the tests, but can we improve it?
We can see repeating part of code here
```java
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt() -> Double.parseDouble()
}
```
And if we want to change it, we would need to do so in 2 places. That's considered bad practice. You can read about ***DRY*** principle [here](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
So let's extract this code to its own method called `parseNumber`.
```java
static double parseNumber(StringBuilder number) {
return number.isEmpty() ? 0.0 : Double.parseDouble(number.toString());
}
```
It's a `static` method, because we can't call a non-static method from a static context which is `main` method. If we didn't have `static` keyword in `parseNumber` method we would have gotten an error.
```bash
$ javac SumDouble.java
SumDouble.java:13: error: non-static method parseNumber(StringBuilder) cannot be referenced from a static context
sum += parseNumber(number); // method call
^
SumDouble.java:18: error: non-static method parseNumber(StringBuilder) cannot be referenced from a static context
sum += parseNumber(number); // method call
^
2 errors
```
Also we need to discuss the ternary operator. It is a shortened form of `if`-statement. Its structure looks like this
```java
(/* condition */) ? (/* do if true */) : (/* do if false */)
```
First argument states a condition. If the condition is `true`, then the second block gets executed. If the condition if `false`, then the third block gets executed. It will work the same as
```java
if (/* condition */) {
/* do if true */
} else {
/* do if false */
}
```
So here's the updated code
```java
// SumDouble.java
public class SumDouble {
public static void main(String[] args) {
double 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); // method call
number = new StringBuilder();
}
}
sum += parseNumber(number); // method call
}
System.out.println(sum);
}
static double parseNumber(StringBuilder number) {
return number.isEmpty() ? 0.0 : Double.parseDouble(number.toString());
}
}
```

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.

View File

@@ -0,0 +1,42 @@
// SumLongOctal.java
import java.math.BigInteger;
public class SumLongOctal {
public static void main(String[] args) {
long 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();
int numberStringLength = numberString.length();
if (numberString.endsWith("o") || numberString.endsWith("O")) {
return new BigInteger(
number.substring(0, numberStringLength - 1),
8
).longValue();
} else {
return Long.parseLong(numberString);
}
} else {
return 0;
}
}
}

View File

@@ -0,0 +1,261 @@
---
title: SumLongOctal
weight: 6
---
# Task
- The input data is in 64 bit decimal numbers
- Octal numbers has suffix `"o"`
- Class should be named `SumLongOctal`
## Solution
We need to change our `parseNumber` method yet again. Let's take a look at it from `SumHex` modification
```java
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;
}
}
```
Let's adapt our new logic on a concept level
```java
static long parseNumber(StringBuilder number) {
if (!number.isEmpty()) {
String numberString = number.toString();
int numberStringLength = numberString.length();
if (
/* number is octal */
) {
return Long.parseLong(numberString.substring(0, numberStringLength - 1), 8); // 16 -> 8
} else {
return Integer.parseInt(numberString);
}
} else {
return 0;
}
}
```
As you can see is that we need to only change method of checking if the number is octal. It's actually fairly easy. With hexadecimal numbers we checked if the number ***started with*** `"0x"` or `"0X"`. Now we need to check if the number ***ends with*** `"o"` or `"O"`. To do that we will use [`String.endsWith(String suffix)`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#endsWith-java.lang.String-) method that checks if the string ends with `prefix`.
And also we change `16` to `8` here
```java
return Long.parseLong(numberString.substring(0, numberStringLength - 1), 8);
```
Because we are converting from octal, not hexadecimal anymore.
> [!NOTE]
> Note, that we alse changed how we crop out the last character of our `numberString`. First, we introduced new variable `numberStringLength`, that represents a length of `numberString` (or number of characters in the string). Then, we take a substring using `substring(0, numberStringLength - 1)`. We start at first index (`0`) and go all the way to the second from the end of the string. We use `- 1` and not `- 2` because last index is exclusive (meaning it doesn't count).
So the code for `parseNumber` method will be
```java
static long parseNumber(StringBuilder number) {
if (!number.isEmpty()) {
String numberString = number.toString();
int numberStringLength = numberString.length();
if (
numberString.endsWith("o") || numberString.endsWith("O")
) {
return Long.parseLong(numberString.substring(0, numberStringLength - 1), 8); // 16 -> 8
} else {
return Integer.parseInt(numberString);
}
} else {
return 0;
}
}
```
So the solution would be
```java
// SumLongOctal.java
public class SumLongOctal {
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();
int numberStringLength = numberString.length();
if (numberString.endsWith("o") || numberString.endsWith("O")) {
return Long.parseLong(numberString.substring(0, numberStringLength - ), 8);
} else {
return Integer.parseInt(numberString);
}
} else {
return 0;
}
}
}
```
Let's try to test it with test like this
```bash
$ java SumLongOctal " +12345678901234567 "
Exception in thread "main" java.lang.NumberFormatException: For input string: "+12345678901234567"
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 sum.SumLongOctal.parseNumber(SumLongOctal.java:32)
at sum.SumLongOctal.main(SumLongOctal.java:15)
```
As you can see we got a `NumberFormatException`. Here it's not an octal number and it doesn't fit into `int` data type, so let's change `int` to `long` which is bigger.
```java
// SumLongOctal.java
public class SumLongOctal {
public static void main(String[] args) {
long sum = 0; // int -> long
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();
int numberStringLength = numberString.length();
if (numberString.endsWith("o") || numberString.endsWith("O")) {
return Long.parseLong(numberString.substring(0, numberStringLength - 1), 8);
} else {
return Long.parseLong(numberString); // Integer.parseInt(...) -> Long.parseLong(...)
}
} else {
return 0;
}
}
}
```
> [!NOTE]
> Notice we changed data type of `sum` to `long` in order to store 64 bit numbers.
Let's test some more
```bash
$ java SumLongOctal.java "1777777777777777777776o"
Exception in thread "main" java.lang.NumberFormatException: For input string: "1777777777777777777776" under radix 8
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Long.parseLong(Long.java:709)
at SumLongOctal.parseNumber(SumLongOctal.java:29)
at SumLongOctal.main(SumLongOctal.java:18)
```
We can see that this number doesn't fit into `long` data type. So we need to use something called [`BigInteger`](https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html) and more specifically we will be using its [`BigInteger.longValue()`](https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html#longValue--) method.
First we need to ***import*** it using
```java
import java.math.BigInteger;
```
and then we will replace
```java
return Long.parseLong(numberString.substring(0, numberStringLength - 1), 8);
```
with
```java
return new BigInteger(number.substring(0, numberStringLength - 1), 8).longValue();
```
So the final code would be
```java
// SumLongOctal.java
import java.math.BigInteger;
public class SumLongOctal {
public static void main(String[] args) {
long 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();
int numberStringLength = numberString.length();
if (numberString.endsWith("o") || numberString.endsWith("O")) {
return new BigInteger(
number.substring(0, numberStringLength - 1),
8
).longValue();
} else {
return Long.parseLong(numberString);
}
} else {
return 0;
}
}
}
```

View File

@@ -489,63 +489,3 @@ public class Sum {
```
This is it! The code is ready.
---
Below are some modifications to the code for deeper understanding of basic language constructions primarily data types. It is always advised to try completing them by yourself before reading the solution. Modifications progress in difficulty from easy to hard.
# SumDouble
- Input data is 64-bytes floating point numbers.
- Class should be named `SumDouble`.
## Solution
This modification is fairly easy. All we need to do is to replace all integers with floating point numbers.
> [!NOTE]
> Here we can see all of the benefits of using `Character.isWhitespace()`, because if we had had for example a set of digits and check if the character is in that set we would need to add `.` (point) to that set to count floating point numbers as well.
So here's the updated code
```java
// SumDouble.java
public class SumDouble {
public static void main(String[] args) {
double sum = 0; // int -> double
for (String argument : args) {
StringBuilder number = new StringBuilder();
for (char c : argument.toCharArray()) {
if (!Character.isWhitespace(c)) {
number.append(c);
} else {
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt -> Double.parseDouble())
}
number = new StringBuilder();
}
}
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt() -> Double.parseDouble()
}
}
System.out.println(sum);
}
}
```
This code will work fine and pass all the tests, but can we improve it?
We can see repeating part of code here
```java
if (!number.isEmpty()) {
sum += Double.parseDouble(number.toString()); // Integer.parseInt() -> Double.parseDouble()
}
```
---