1.2. Implementation

LongInteger: implementation.

We are going to create a class called LongIntegerarrow-up-right inheriting SignedNumeral that can store an indefinite size of an integer value beyond the primitive typesarrow-up-right such as int and long.

What is so special about primitive data types in Java?

circle-info

Java SE provides a similar class called BigIntegerarrow-up-right although the implementations of LongIntegerandBigIntegerare completely independent.

Field

Let us declare the member field digits that is an array of bytes holding the values of this integer:

public class LongInteger extends SignedNumeral<LongInteger> {
    /** The values of this integer (excluding the sign). */
    protected byte[] digits;
    ...
  • L1: LongInteger is passed to specify the generic type T in SignedNumeral.

The i'th dimension of digits is the i'th least significant digit in the integer such that the integer 12345 would be stored as digits = {5, 4, 3, 2, 1}, which makes it convenient to implement the arithmetic methods, add() and multiply().

Is the array of bytes the most efficient way of storing a long integer?

Constructors

Let us define the following three constructors:

/** Creates a long integer with the default value of "0". */
public LongInteger() {
    this("0");
}

/**
 * Creates a long integer by copying the specific object.
 * @param n the object to be copied.
 */
public LongInteger(LongInteger n) {
    super(n.sign);
    digits = Arrays.copyOf(n.digits, n.digits.length);
}

/**
 * Creates a long integer with the specific sign and values.
 * @param n the sign and values to be set.
 * @see #set(String)
 */
public LongInteger(String n) {
    set(n);
}
  • L2: the default constructor that initializes this integer with 0 by calling the constructor in L20.

  • L10: a copy constructor that initializes this integer with n.

    • super(): calls the corresponding constructor in the super class, SignedNumeral.

    • Arrays.copyOf()arrow-up-right: creates a new array by copying n.digits.

  • L20: a constructor that initializes this integer with n by passing it to the set() method.

Do the constructors in L10 and L20 call any constructor in the super class?

Arrays.copyOf() is a static methodarrow-up-right referenced by the class type Arrays, not an object. Java provides many classes with static methods that are commonly used (e.g., Arraysarrow-up-right, Collectionsarrow-up-right).

Can you call non-static methods or fields in the body of a static method?

circle-info

The static keyword must not be abused to quickly fix compile errors unless it is intended.

Method: set()

Let us define the set() method that takes a string and sets the sign and the value of this integer:

When should we use throwarrow-up-right statements over try..catcharrow-up-right blocks for error handling and vice versa?

circle-info

This type of method is called a setter. Java encourages making member fields private and creating getters and setters to access the fields for encapsulationarrow-up-right, which is not necessarily encouraged by other languages.

Method: add()

Let us override the add() method that calls two helper methods:

  • L3-4: adds n to this integer that has the same sign by calling addSameSign().

  • L5-6: adds n to this integer that has a different sign by calling addDifferentSign().

The following shows an implementation of addSameSign() based on the simple arithmetic:

  • L7-9: creates the byte array result by copying values in this integer.

  • L12-19: adds n to results (if exists) from the least significant digit.

    • L15-18: pass a carry to the next digit.

  • L22: trims the most significant digit if it is 0.

What are tradeoffs to make the size of result to be m instead of m+1 and vice versa?

The following shows addDifferentSign() that throws UnsupportedOperationExceptionarrow-up-right:

The implementation of addDifferentSign() is quite similar to addSameSign() although it involves a few more logics. We will leave this as an exercise.

circle-info

In practice, addSameSign()and addDifferentSign() should be private. We made them protected for exercise purposes.

Method: multiply()

Let us override the multiply() method:

  • L4: sets the sign after the multiplication.

  • L7-15: multiplies n to this integer:

    • L7: the max-dimension of results is digits.length + n.digits.length.

    • L12-13: pass a carry to the next digit.

  • L18-20: trims the most significant digit iteratively if it is 0.

    • L20: ++m increments m before the comparison.

What is the worst-case complexity of the multiply() method?

Method: main()

Let us create a runnable class called LongIntegerRunarrow-up-right that contains the main method:

Can we define the main method in LongInteger instead without creating LongIntegerRun?

  • L2: the parameter args is passed from the command line.

Why does the main method need to be static?

This prints something like the following:

  • [: one-dimensional array.

  • L: the element of this array is an object.

  • java.lang.String: the type of object.

  • d716361: the hash code of this array in hexadecimal.

What is the hash code of an object?

Every object implicitly inherits Objectarrow-up-right that defines a few member methods including toString()arrow-up-right, which gets called automatically by the println() method to retrieve the string representation of this object. We can use the helper method Arrays.toString()arrow-up-right that gives a more readable representation:

How is the Arrays.toString() method implemented?

Since no argument is passed to the main method at the moment, this prints an empty array:

If you set the arguments to 123 -456 using the [Run - Edit Configurations - Program arguments]setting, it prints the following array:

Given those two arguments, we can create two integers:

This prints something like the following, which are returned by a.toString():

How is the toString() method implemented in the Object class?

Method: toString()

To print a more readable representation, we need to override the toString() method in LongInteger:

What are the advantages of using StringBuilder instead of concatenating values with the + operator as follows:

Given the overridden method, the above main method now prints the following:

What are the advantages of overriding toString() instead of creating a new method with the same code, and calling the new method to get the string representation of LongInteger?

Method: compareTo()

Java does not allow operator overloadingarrow-up-right, so it is not possible to use logical operators to compare the two integers above, a and b:

In fact, any object that is comparable must inherit the interface Comparablearrow-up-right as follows:

  • L2: LongInteger is passed to Comparable as a generic type.

Is extends always used to inherit a class whereas implements is used to inherit an interface?

The Comparable interface contains one abstract method called compareTo()arrow-up-right that returns a negative value if this object is smaller than n, a positive value if this object is greater than n, and zero if this object equals to n. The compareTo() method must be overridden by the LongInteger class:

The compareAbs() method compares the absolute values of this and n:

  • L7: if digits has more dimensions, its absolute value is greater.

  • L10-13: compares the significant digits iteratively.

Is it safe to use the same variable i to iterate both digits and n.digits?

Once LongInteger properly inherits Comparable by overriding compareTo(), objects instantiated by this class can be compared using many built-in methods.

What is the advantage of declaring list as List instead of ArrayList? What kind of sorting algorithm does Collections.sort() use?

The above code prints the following sorted lists:

What would be the case that needs to distinguish -0 from 0?

Last updated

Was this helpful?