Core Java APIs

Creating and Manipulating Strings

A String is a fundamental reference type.
It implements the interface CharSequence. This interface is a general way of representing several classes, including String and SringBuilder.

These two snippets both create a String.

String name = "Fluffy";
String name = new String("Fluffy");

Concatenation

The exam creatos like String concatenation because the + operator can be used in two ways within the same line of code.

Rules:

  1. If both operands are numeric + means numeric addition
  2. If either operand is a String + means concatenation
  3. The expression is evaluated from left to right
System.out.println(1 + 2); // 3
System.out.println("a" + "b"); // ab
System.out.println("a" + "b" + 3); // ab3
System.out.println(1 + 2 + "c"); //3c
System.out.println("c" + 1 + 2); //c12

If you see questions like this, just take your time and check the types.

int three = 3;
String four = "4";
System.out.println(1 + 2 + three + four);
String s = "1";
s += "2";
s += 3;
System.out.println(s);

Immutability

A String is immutable. Once a String object is created, it’s not allowed to change. It cannot be made larger nor smaller, and you cannot change one of the characters inside it.

String s1 = "1";
String s2 = s1.concat("2");
s2.concat("3");
System.out.println(s2); // prints "12" because String is immutable

Important String Methods

For all these methods, you need to remember that a String is a sequence of characters anda Java counts from 0 when indexed.

length()

int length();

It returns the number of characters in the String.

String string = "animals";
System.out.println(string.length()); // 7

Strings counts from 0, only when you’re using indexes or positions within a list. When determining the total size or length, Java uses normal counting.

charAt()

char charAt(int index);

It lets you query the String to find out what character is at a specific index.

String string = "animals";
System.out.println(string.charAt(0)); // a
System.out.println(string.charAt(6)); // s
System.out.println(string.charAt(7)); // throws Exception

indexOf()

int indexOf(int ch);
int indexOf(int ch, int fromIndex);

int indexOf(String str);
int indexOf(String str, int fromIndex);

It looks at the chars in the string and finds the first index that matches the desired value.

It can work with an individual char or a whole String as input.
It can also start from a requested position.

String string = "animals";
System.out.println(string.indexOf('a')); // 0
System.out.println(string.indexOf("al")); // 4
System.out.println(string.indexOf('a', 4)); // 4
System.out.println(string.indexOf("al", 5)); // -1

It returns -1 when no match is found.

substring()

String substring(int beginIndex);
String substring(int beginIndex, int endIndex);

It also looks for charactes in a String. It returns part of the String. The first parameter is the index to start with for the returned String. This is zero-based index. There’s an optional second parameter which is the index you want to stop at. It’s “stop at”, not “include”. The endIndex parameter is allowed to be 1 past the end of the sequence if you want to stop at the end of the sequence. That woul be redundant though since you could omit the second parameter in that case. Don’t be surprised if the exam uses it.

The method returns the string starting from the requested index. If an index is requested, it stops right before that index. Otherwise, it goes to the end of the String.

String string = "animals";
System.out.println(string.substring(3)); // mals
System.out.println(string.substring(string.indexOf('m'))); // mals
System.out.println(string.substring(3, 4)); // m
System.out.println(string.substring(3, 7)); // mals
System.out.println(string.substring(3, 3)); // empty String
System.out.println(string.substring(3, 2)); // throws Exception
System.out.println(string.substring(3, 8)); // throws Exception

The substring() method is the trickets String method on the exam.

toLowerCase() and toUpperCase()

String toLowerCase();
String toUpperCase();

These methods do exactly what they say. They convert any lowercase characters to uppercase and viceversa.

String string = "animals";
System.out.println(string.toUpperCase()); // ANIMALS
System.out.println("Abc123".toLowerCase()); // abc123

equals() and equalsIgnoreCase()

boolean equals(Object obj);
boolean equalsIgnoreCase(String str);

The method equals() check whether two String objects contain exactly the same characters in the same order. It takes an Object instead of an String. This is because the method is the same for all objects. If you pass something that isn’t a String, it will just return false.

The method equalsIgnoreCase() checks whether two String objects contain the same characters without considering characters’ cases.

System.out.println("abc".equals("ABC"));  // false
System.out.println("ABC".equals("ABC")); // true
System.out.println("abc".equalsIgnoreCase("ABC")); // true

startsWith() and endsWith()

boolean startsWith(String prefix);
boolean endsWith(String suffix);

They look at whether the provided valeu matches part of the String.

System.out.println("abc".startsWith("a")); // true
System.out.println("abc".startsWith("A")); // false
System.out.println("abc".endsWith("c")); // true
System.out.println("abc".endsWith("a")); // false

replace()

String replace(char oldChar, char newChar);
String replace(CharSequence target, CharSequence replacement);

It does a simple search and replace on the String. There’s a version that takes char parameters as well as a version that takes CharSequence parameters.

System.out.println("abcabc".replace('a', 'A')); // AbcAbc
System.out.println("abcabc".replace("a", "A")); // AbcAbc

contains()

boolean contains(CharSequence charSeq);

It looks for matches in the String. The match can be anywhere in the String. It’s case sensitive.

System.out.println("abc".contains("b")); // true
System.out.println("abc".contains("B")); // false

trim(), strip(), stripLeading(), stripTrailing()

String strip();
String stripLeading();
String stripTrailing();

String trim();

Removes blank space from the begginning and/or end of a String. strip() and trim() remove whitespace from the beginning and end of a String. Whitespace consists of spaces along with the \t, \r and \n characters.

The strip() method is new in Java11. It does everything that trim() does, but it supports Unicode.

Additionally, stripLeading() and stripTrailing() methods were added in Java11. stripLeading() method removes whitespace from the beginning of the String and leaves it at the end. The stripTrailing()method does the opposite. It removes from the end and leaves it at the beginning.

System.out.println("abc".strip()); // abc
System.out.println("\t a b c\n".strip()); // a b c

String text = " abc\t ";
System.out.println(text.trim().length()); // 3
System.out.println(text.strip().length()); // 3
System.out.println(text.stripLeading().length()); // 5
System.out.println(text.stripTrailing().length()); // 4

Remember \t is a single character.

intern()

String intern();

It returns the value from the string pool if it’s there. Otherwise it adds the value to the string pool.

Method Chaining

On the exam there’s a tendency to cram as much code as possible into a small space. You’ll see a lot of method chaining such as

String result = "AniMaL     ".trim().toLowerCase().replace('a', 'A');
System.out.println(result);

Remember that String is immutable.

String a = "abc";
String b = a.toUpperCase();
b = b.replace("B", "2").replace('C', '3');
System.out.println("a=" + a); // "abc"
System.out.println("b=" + b); // "A23"

Using the StringBuilder Class

String is immutable. For every iteration of the following loop, a new String object is created and the old one becomes eligible for garbage collection. After 26 iterations, a total of 27 objects are instantiated.

String alpha = "";
for(char current = 'a'; current <= 'z'; current++)
	alpha += current;
sout(alpha);

This is a far better solution.

StringBuilder alpha = new StringBuilder();
for(char current = 'a'; current <= 'z'; current++)
	alpha.append(current);
sout(alpha);

Mutability and Chaining

StringBuilder is not immutable. The exam will likely try to trick you with respect to String and StringBuilder being mutable.

When we chain String method calls, the result is a new String with the answer. StringBuilder changes its own state and returns a reference to itself.

1: StringBuilder sb = new StringBuilder("start");
2: sb.append("+middle"); // sb = "start+middle"
3: StringBuilder same = sb.append("+end"); "start+middle+end"

Line 2 adds text to the end of sb and returns a reference, which is ignored. Line 6 also adds text to the end of sb a returns a reference, which is stored in same. Which means sb and same point to the same object and would print the same.

StringBuilder a = new StringBuilder("abc");
StringBuilder b = a.append("de");
b = b.append("f").append("g");
sout(a);
sout(b);

At this example, there’s only one object. The result is both point to the same object and both print abcdefg.

Creating a StringBuilder

There’re three ways to construct a StringBuilder.

StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder("animal");
StringBuilder sb3 = new StringBuilder(10);

The third one tells Java that we have some idea of how big the eventual value will be, and how StringBuilder should reserve a certain capacity for it.

Important StringBuilder Methods

charAt(), indexOf(), length(), substring()

These four methods work exactly the same as for String.

StringBuilder sb = new StringBuilder("animals");
String sub = sb.substring(sb.indexOf("a"), sb.indexOf("al"));
int len = sb.length();
char ch = sb.charAt(6);
sout(sub + " " + len + " " + ch);

The correct answer is anim 7 s.
indexOf() return 0 and 4. substring() return the String starting with index 0 and ending right before index 4. length() returns 7 because it’s the number of chars rather than an index. charAt() returns the char at index 6. Here we do start with 0 because we’re referring to indexes.
Notice that substring() returns a String rather than a StringBuilder. It’s a method that inquires about what the state of the StringBuilder happens to be.

append()

StringBuilder append(String str); // there're more for more data types.

This is the most frequently used method. It does what it soulds like. It adds the param to the StringBuilder and returns a reference to the current StringBuilder.

insert()

StringBuilder insert(int offset, String str); // there're more for more data types.

It adds chars to the StringBuilder at the requested index and returns a reference to the current StringBuilder.

StringBuilder sb = new StringBuilder("animals");
sb.insert(7, "-"); // animals-
sb.insert(0, "-"); // -animals-
sb.insert(4, "-"); // -ani-mals-

(!) The exam creators will try to trip you up on this. As we add and remove chars, their indexes change. When you see a question such as this, draw what’s going on so you won’t be confused. (!)

delete() and deleteCharAt()

StringBuilder deleteCharAt(int index);
StringBuilder delete(int startIndex, int endIndex);

This is the opposite of insert(). It removes chars from the sequence and returns a reference to the current StringBuilder. This method is convenient when you want to delete only one character.

StringBuilder sb = new StringBuilder("abcdef");
sb.delete(1, 3); // adef
sb.deleteCharAt(5); // throws an exception.

The delete() method is more flexible than some others when it comes to array indexes. If you specify a second param that’s past the end of the StringBuilder, java will assume you meant the end.

This is valid code.

StringBuilder sb = new StringBuilder("abcdef");
sb.delete(1, 100); // a

replace()

StringBuilder replace(int startIndex, int endIndex, String newString);

This method works differently for StringBuilderthan for String.

StringBuilder builder = new StringBuilder("pigeon dirty");
builder.replace(3, 6, "sty");
sout(builder); // pigsty dirty

First, Java deletes ther chars starting with index 3 and ending right before index 6. Then it inserts the value of sty.

StringBuilder builder = new StringBuilder("pigeon dirty");
builder.replace(3, 100, "");
sout(builder);

This prints pig. The method first does a delete and replace() allows specifying a second param that’s past the end of the StringBuilder.

reverse()

StringBuilder reverse();

It does exactly what it sounds like. It reverses the chars sequence and returns a reference to the current StringBuilder.

StringBuilder sb = new StringBuilder("ABC");
sb.reverse();
sout(sb);

This will print CBA.

toString()

String toString();

This converts a StringBuilder into a String.

StringBuilder sb = new StringBuilder("ABC");
String s = sb.toString();

Understanding Equality

Comparing equals() and ==

StringBuilder one = new StringBuilder();
StringBuilder two = new StringBuilder();
StringBuilder three = one.append("a");
sout(one == two);  // false
sout(one == three); // true

Since we aren’t dealing with primitives, == checks for references. The last check works as StringBuilder methods like to return the current reference for chaining.

String x = "Hello World";
String z = "Hello World".trim();
sout(x.equals(z)); // true

This checks the inside of the String rather than the reference itself. If a class doesn’t have an equals method, Java determines whether the references point to the same object - which is exactly what == does. StringBuilder does not implement equals() so when using it, it checks for reference instead of content.

String string = "a";
StringBuilder builder = new StringBuilder("a");
sout(string == builder); // DOES NOT COMPILE. NOT SAME TYPE.

The String Pool

Since String are everywhere, they use up a lot of memory. As many Strings repeat in the program, java reuses common ones. The string pool, also known as the intern pool, is a location in the JVM that collects all these Strings.

Let’s visit a more complex and confusing scenario, String equality, made so in part because of the way the JVM reuses String literals.

String x = "Hello World";
String y = "Hello World";
System.out.println(x == y); // true

Strings are immutable and literals are pooled. The JVM created only one literal in memory and both x and y point to the same location in memory.
It gets trickier.

String x = "Hello World";
String y = new String("Hello World");
sout(x == y); // false

(!) Here the former says to use the String pool normally, but the second option with an explicit new says to not use the string pool amd create a new object, even if it’s less eficient. (!)

String x = "Hello World";
String z = " Hello World".trim();
sout(x == z); // false

In this example, we don’t have two of the same String literal as one is computed at runtime and the other at compile-time.

You can also do the opposite and tell Java to use the String pool. This is done with the intern() method. It will use an object from the pool if one is present. It it’s not present, Java will add it this time.

String name = "Hello World";
String name2 = new String("Hello World").intern();
sout(name == name2); // true

Understanding Java Arrays

String and StringBuilder are implemented using an array of characters. An array is an area of memory on the heap with space for a designated number of elements. An array is an ordered list. It can contain duplicates.

StringBuilder is implemented as an array where the array object is replaced with a new bigger array object when it runs out of space to store characters.

Creating an Array of Primitives

This is most common way to create an array.

int[] numbers1 = new int[3];

When you use this form to instantiate an array, all elements are set to default value for that type.
The indexes start with 0 and count up, just as they did for a String.

Another way to create an array, is to specify all the elements it should start out with

 int[] numbers2 = new int[] {42, 55, 59}; // this is redundant
 int[] numbers2 = {42, 55, 59}; // anonymous array

Beware multiple “Arrays” in declaration

 // this declares two int arrays
 int[] ids, types;

 // this declares ONE int array and ONE int
 int ids[], types;

Creating an Array with Reference Variables

 String[] bugs = { "cricket", "beetle" };
 sout(bugs.toString()); // [Ljava.lang.String;@160bcc9]

The .equals() method on arrays does not look at the elements of the array.
The .toString() method shows the following. [L means it’s an array. java.lang.String is the reference type and 160bcc9 is the hascode.

(!) The array does not allocate space for the String objects. Instead, it allocates space for a reference to where the objects are really stored. (!)

 String[] strings = { "stringValue" };
 Object[] objects = strings;
 String[] againString = (String[]) objects;

 againString[0] = new StringBuilder(); // DOES NOT COMPILE
 objects[0] = new StringBuilder(); // (!)

The last line will compile, but will throw a runtime error (ArrayStoreException).

Using an Array

 String[] mammals = {"monkey", "chimp", "donkey"};
 sout(mammals.length);
 sout(mammals[0]);
 sout(mammals[1]);
 sout(mammals[2]);

(!) Watch out for ArrayIndexOutOfBoundsException. (!)

 int numbers = new int[10];
 // this line tries to access 10 indexes. only 0-9 are valid!
 for(int i = 0; i<= numbers.length; i++)
 	numbers[i] = i + 5;

Sorting

You can pass almost any array to Arrays.sort().

 int[] numbers = {6,9,1};
 Arrays.sort(number);

This will print 6 9 1 as expected.

Watch out when sorting Strings

 String[] strings = {"10","9","100"};
 Arrays.sort(strings);

This time the code outputs 10 100 9. The problem is that String sorts in alphabetic order and 1 sorts before 9.

Java provides a convenient way to search - only if the array is already sorted.

 1: int[] numbers = {2,4,6,8};
 2: sout(Arrays.binarySearch(numbers, 2)); // 0
 3: sout(Arrays.binarySearch(numbers, 4)); // 1
 4: sout(Arrays.binarySearch(numbers, 1)); // -1
 5: sout(Arrays.binarySearch(numbers, 3)); // -2
 6: sout(Arrays.binarySearch(numbers, 9)); // -5

If the target is not found in the sorted array, it shows the negative value showing one smaller than the negative of the index. Take note that we need a sorted Array. If it wasn’t, we couldn’t apply the rules.

Line 2 and 3 both return the found index.
Line 4 searches for the index of 1. It isn’t at the list, altough it can determine that it should be inserted at element 0 to.
Line 5 is similar. Altough the element 3 isn’t in the list, it would need to be inserted at element 1 to preserve the sorted order.
Line 6 tells us that 9 should be inserted at index 4.

Comparing

Java also provides methods to compare two arrays to arrays to determine which is “smaller”.

compare()

There’re a bunch of rules to learn before using it.

This are the rules to compare a single value. First you need to learn what the return value means.

  • A negative number means the first array is smaller than the second.
  • A zero means the arrays are equal.
  • A positive number means the first array is larger than the second.
sout(Arrays.compare(new int[] {1}, new int[] {2}));

This code prints a negative number.

This are the rules on how to compare arrays of different lengths:

  • If both arrays are the same length and have the same values in each spot, in the same order, return zero.
  • If all the elements are the same but the second array has extra elements at the end, return a negative number.
  • If all the elements are the same but the first array has extra elements at the end, return a positive number.
  • If the first element that differs is smaller in the first array, return a negative number.
  • If the first element that differs is larger in the first array, return a positive number.

What does smaller mean?

  • null is smaller than any other value.
  • For numbers, normal numeric order applies.
  • For Strings, one is smaller if it’s a prefix of another.
  • For Strings/chars, numbers are smaller than letters.
  • For Strings/chars, uppercase is smaller than lowercase.
First array Second array Result Reason
new int[]{1,2} new int[]{1} positive number The first array is longer
new int[]{1,2} new int[]{1,2} zero exact match
new String[]{“a”} new String[]{“aa”} negative number the first element is a substring of the second
new String[]{“a”} new String[]{“A”} positive number uppercase is smaller than lowercase
new String[]{“a”} new String[]{null} positive number null is smaller than a letter

Finally, this code does not compile because the types are different. When comparing two arrays, they must be the same type.

Arrays.compare(new int[] {1}, new String[] {"a"}); // DOES NOT COMPILE

mismatch()

If the arrays are equal, mismatch() returns -1. Otherwise, it returns the first index where they differ.

sout(Arrays.mismatch(new int[] {1}, new int[] {1})); // -1
sout(Arrays.mismatch(new String[] "a", new String[] {"A"})); // 0
sout(Arrays.mismatch(new int[] {1,2}, new int[] {1})); // 1

The last case is special. One array has an element at index 1 and the other does not, therefore the result is 1.

equals vs comparison vs mismatch

|method|when arrays are the same|when arrays differ| |:—:|:—:|:—:| |equals()|true|false| |compare()|0|positive or negative number| |mismatch()|-1|zero or positive index|

Varargs

When you’re passing an array to your method, you can do it with varargs.

public static void main(String... args);

Multidimensional Arrays

Creating a Multidimensional Array

int[][] vars; // 2D array
int vars [][]; // 2D array
int[] vars[]; // 2D array
int[] vars[], vars[][]; // 2D array AND 3D array

They don’t need to be rectangular in shape.

String[][] rectangle = new String[3][2];
int[][] differentSizes = \{\{1,4}, {3}, {9,8,7}};

Using a multidimensional Array

int[][] twoD = new int[3][2];
for(int i = 0; i < twoD.length;i++) {
	for(int j = 0; j < twoD[i].length; j++)
		System.out.print(twoD[i][j] + " "); // print element
	System.out.println(); // print new row
}

Understanding an ArrayList

Just like a StringBuilder an ArrayList can change capacity at runtime as needed. It’s an ordered sequence that allows duplicates. It’s inside java.util.ArrayList package.

Creating an ArrayList

As with StringBuilder, there’re three ways to create an ArrayList.

// all this versions are pre Java5
ArrayList list = new ArrayList();
ArrayList list2 = new ArrayList(10);
ArrayList list3 = new ArrayList(list2);

// from Java5 on (generics)
ArrayList<String> list4 = new ArrayList<String>();
// from Java7 on
ArrayList<String> list5 = new ArrayList<>();

The first says to create it containing space for the default number of elements, but not to fill any slots yet.

The second says to create it containing a specific number of slots, but again not to assign any.

Using var with ArrayList

Consider mixing var with ArrayList and generics.

var strings = new ArrayList<String>();
strings.add("a");
// the type of var is ArrayList<String>

var strings = new ArrayList<>(); // DOES COMPILE
strings.add("a");
// the type of var is ArrayList<Object>

Why doesn’t this compile?

var list = new ArrayList<>();
list.add("a");
for (String s : list) { } // DOES NOT COMPILE

The type for list is ArrayList<Object> so adding a String inside is fine, but afterwards inside the for we’d need to set it to Object and not String.

ArrayList extends from a List interface.

List<String> list = new ArrayList<>(); // fine
ArrayList<String> list2 = new List<>(); // DOES NOT COMPILE

Using an ArrayList

add()

It inserts a new value into the ArrayList.

boolean add(E element);
void add(int index, E element);

The boolean always returns true. It’s there for compatibility.

ArrayList list = new ArrayList();
list.add("hawk");
list.add(Boolean.TRUE);
sout(list); // [hawk, TRUE]

Without generics, the type is ArrayList<Object> so we may store whatever inside. Let’s use now generics.

ArrayList<String> list = new ArrayList();
list.add("hawk");
list.add(Boolean.TRUE); // DOES NOT COMPILE

Let’s add now inside a specific index

List<String> birds = new ArrayList();
birds.add("hawk"); // [hawk]
birds.add(1, "robin"); // [hawk, robin]
birds.add(0, "blue jay"); // [blue jay, hawk, robin]
birds.add(1, "cardinal"); // [blue jay, cardinal, hawk, robin]

(!) When a question has code that adds objects at indexed positions, draw it so that you won’t lose track of which value is at which index. (!)

remove()

It removes the first matching value in the ArrayList or remove the element at a specified index.

boolean remove(Object object);
E remove(int index);

The boolean tells whether a match was removed. The E return type is the element that was actually removed.

List<String> birds = new ArrayList<>();
birds.add("hawk"); // [hawk]
birds.add("hawk"); // [hawk, hawk]
sout(birds.remove("cardinal")); // false
sout(birds.remove("hawk")); // true
sout(birds.remove(0)); // hawk

Using an intthat doesn’t exist will throw an IndexOutOfBoundsException. There’s also a removeIf() method.

set()

It changes one of the elements of the ArrayList without changing the size.

E set(int index, E newElement);

The E return type is the element that got replaces.

List<String> birds = new ArrayList<>();
birds.add("hawk"); // [hawk]
sout(birds.size()); // 1
birds.set(0, "robin"); // [robin]
sout(birds.size()); // 1
birds.set(1, "robin"); // IndexOutOfBoundsException

isEmpty() and size()

They look at how many of the slots are in use.

boolean isEmpty();
int size();

Usage:

List<String> birds = new ArrayList<>();
sout(birds.isEmpty()); // true
sout(birds.size()); // 0
birds.add("hawk"); // [hawk]
birds.add("hawk"); // [hawk, hawk]
sout(birds.isEmpty()); // false
sout(birds.size()); // 2

clear()

It provides an easy way to discard all elements of the ArrayList.

void clear();

Usage:

List<String> birds = new ArrayList<>();
birds.add("hawk"); // [hawk]
birds.add("hawk"); // [hawk, hawk]
sout(birds.isEmpty()); // false
sout(birds.size()); // 2
birds.clear();
sout(birds.isEmpty()); // true
sout(birds.size()); // 0

contains()

It checks whether a certain value is in the ArrayList.

boolean contains(Object object)

Usage:

List<String> birds = new ArrayList<>();
birds.add("hawk"); // [hawk]
sout(birds.contains("hawk")); // true
sout(birds.contains("robin")); // false

The method calls equals() on each element of the ArrayList to see whether there’re any matches.

equals()

ArrayList has a custom implementation of equals(). You can compare two lists to see whether they contain the same elements in the same order.

boolean equals(Object object);

Usage:

List<String> one = new ArrayList<>();
List<String> two = new ArrayList<>();
sout(one.equals(two)); // true
one.add("a"); // [a]
sout(one.equals(two)); // false
two.add("a"); // [a]
sout(one.equals(two)); // true
one.add("b");    // [a, b]
two.add(0, "b"); // [b, a]
sout(one.equals(two)); // false

Wrapper Classes

Wrapper class is an object type that corresponds to the primitive. Each wrapper class also has a constructor, but isn’t recommended for new code. Use valueOf() instead.

There’re also methods for converting a String to a primitive or wrapper class. The parse methods, such as parseInt() return a primitive, and the valueOf() methods return a wrapper class.

int primitive = Integer.parseInt("123");
Integer wrapper = Integer.valueOf("123");

If the String passed in is not valid for the given type, Java throws a NumberFormatException.

int bad1= Integer.parseInt("a"); // NumberFormatException
Integer bad2 = Integer.valueOf("123.45"); // NumberFormatException
Wrapper Class String to primitive String to wrapper class
Boolean Boolean.parseBoolean(“true”); Boolean.valueOf(“TRUE”);
Byte Byte.parseByte(“1”); Byte.valueOf(“2”);

Wrapper Classes and Null

One advantage of a wrapper class over a primitive is that because it’s an object, it can be used to store a null value.

Autoboxing and Unboxing

Since Java5, you can just type the primitive value, and Java will convert it into the relevant wrapper class for you. This is called autoboxing. The reverse is called unboxing.

List<Integer> weights = new ArrayList<>();
Integer w = 50;
weights.add(w); // [50]
weights.add(Integer.valueOf(60)); // [50, 60]
weights.remove(50); // [60]
double first = weights.get(0); // [60]

If you try to unbox a null, it will throw a NullPointerException.

List<Integer> heights = new ArrayList<>();
heights.add(null);
int h = heights.get(0); // NullPointerException

Storing a null reference is legal, the trouble comes when we try to unbox that null to a primitive int.

Also be careful when autoboxing into Integer.

List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.remove(1);
sout(numbers); // 1

When we request to .remove(1); what we’re actualling requesting is to remove the index 1. If you want to remove the number 1 instead, you’d need something like .remove(new Integer(1)); to force wrapper class use.

Converting between Array and List

ArrayList to Array

Turning an ArrayList into an array.

List<String> list = new ArrayList<>();
list.add("hawk");
list.add("robin");
Object[] objectArray = list.toArray();
String[] stringArray = list.toArray(new String[0]);
list.clear();
sout(objectArray.length); // 2
sout(stringArray.length); // 2

ArrayList knows how to convert itself to an array. The only problem is that it default to an Object[]. This isn’t usually what you want.

The advantage of specifying a size of 0 for the parameter, is that Java will create a new array of the proper size for the return value. If you like, you can suggest a larger array to be used instead. The newly created object has no relationship to the original List. It’s simply a copy.

Array to List - option 1 (backed List)

Converting from array to List is more Interesting. One option is to create a List that’s linked to the original array. When a change is made to one, it’s available in the other. It’s a fixed size list and is also known as a backed list because the array changes with it.

String[] array = { "hawk", "robin" }; // [hawk, robin]
List<String> list = Arrays.asList(array); // returns fixed size list
sout(list.size()); // 2
list.set(1, "test"); // [hawk, test]
array[0] = "new"; // [new, test]
sout(Arrays.toString(array)); // [new, test]
list.remove(1); // UnsupportedOperationException

The line List<String> list = Arrays.asList(array); converts the array to a List. This is no ArrayList, it’s a fixed-size list. Then set() is okay because it replaces an existing value. It updates both array and List because they point to the same data store. Then .remove(1); throws an exception because this is a fixed-size list.

Array to List - option 2 (immutable List)

Another option is to create an immutable List. This means you cannot change the values or the size of the List. You can change the original array, but changes will not be reflected in the immutable List.

String[] array = { "hawk", "robin" }; // [hawk, robin]
List<String> list = List.of(array); // returns immutable list
sout(list.size()); // 2
array[0] = "new";
sout(Arrays.toString(array)); // [new, robin]
sout(list); // [hawk, robin]
list.set(1, "test"); // UnsupportedOperationException

Using varargs to create a List

List<String> list1 = Arrays.asList("one", "two");
List<String> list2 = List.of("one", "two");

Both of these methods take varargs, which let you pass in an array or just type out the String values.

- toArray() Arrays.asList() List.of()
Type converting from List Array (or varargs) Array (or varargs)
Type created Array List List
Allowed to remove values from created object No No No
Allowed to change values in the created object Yes Yes No
Changing values in the created object affects the original or vice versa No Yes N/A

Sorting

Sorting an ArrayList is similar to sorting an array.

List<Integer> numbers = new ArrayList<>();
numbers.add(99);
numbers.add(5);
numbers.add(81);
Collections.sort(numbers); // [5, 81, 99]

Creating Sets and Maps

Introducing Sets

A Set is a collection of objects that cannot contain duplicates.
All the methods you learned for ArrayList apply to a Set with the exception of those taking an index as a patameter. This is because Set is not ordered.

If you try to add() an object which already existed, this method will return false.

The two common classes that implement Set are HashSet and TreeSet. The former is the most common and the last is used when sorting is important.

Set<Integer> set = new HashSet<>();
sout(set.add(66)); // true
sout(set.add(66)); // false
sout(set.size()); // 1
set.remove(66);
sout(set.isEmpty()); // true

Introducing Maps

A Map uses a key to identify values.

key value
George 555-555-5555
Mary 777-777-7777

The most common implementation is HashMap. Some of the methods are the same as those in ArrayList. There’re also methods specific to dealing with key and value pairs such as the following.

Method Description
V get(Object key) Returns the value mapped by key or null if none is mapped
V getOrDefault(Object key, V other) Returns the value mapped by key or other if none is mapped
V put(K key, V value) Adds or replaces key/value pair. Returns previous value or null
V remove(Object key) Removes and returns value mapped to key. Returns null if none
boolean containsKey(Object key) Removes whether key is in map
boolean containsValue(Object key) Returns whether value is in map
Set<K> keySet() Returns set of all keys
Collection<V> values() Returns Collection of all values

Let’s check some examples

Map<String, String> map = new HashMap<>();
map.put("koala", "bamboo");
String food = map.get("koala"); // bamboo
String other = map.getOrDefault("ant", "leaf"); // leaf
for (String key: map.keySet())
	sout(key + " " + map.get(key)); // koala bamboo

Calculating with Math APIs

min() and max()

double min(double a, double b);
float min(float a, float b);
int min(int a, int b);
long min(long a, long b);

They compare two values and return one of them. max() has the same overloaded methods such as min().

int second = Math.min(7, -9); // -9

round()

It gets rid of the decimal portion of the value. If the fractional part is .5 or higher, we round up.

long round(double num);
int round(float num);

There’re two overloaded methods to ensure there’s enough room to store a rounded double if needed.

long low = Math.round(123.45); // 123
long high = Math.round(123.50); // 124
int fromFloat = Math.round(123.45f); // 123

pow()

double pow(double number, double exponent);

It handles exponents. Fractionals exponents are allowed as well.

double squared = Math.pow(5,2); // 25.0

random()

double random();

It returns a value greater than or equal to 0 and less than 1. It cannot be negative.

double num = Math.random();

Summary

In this chapter, you learned that Strings are immutable sequences of characters. The new operator is optional. The concatenation operator (+) creates a new String with the content of the first String followed by the content of the second String. If either operand involved in the + expression is a String, concatenation is used; otherwise, addition is used. String literals are stored in the string pool. The String class has many methods.

StringBuilders are mutable sequences of characters. Most of the methods return a reference to the current object to allow method chaining. The StringBuilder class has many methods.

Calling == on String objects will check whether they point to the same object in the pool. Calling == on StringBuilder references will check whether they are pointing to the same StringBuilder object. Calling equals() on String objects will check whether the sequence of characters is the same. Calling equals() on StringBuilder objects will check whether they are pointing to the same object rather than looking at the values inside.

An array is a fixed-size area of memory on the heap that has space for primitives or pointers to objects. You specify the size when creating it—for example, int[] a = new int[6];. Indexes begin with 0, and elements are referred to using a[0]. The Arrays.sort() method sorts an array. Arrays.binarySearch() searches a sorted array and returns the index of a match. If no match is found, it negates the position where the element would need to be inserted and subtracts 1. Arrays.compare() and Arrays .mismatch() check whether two arrays are the equivalent. Methods that are passed varargs (…) can be used as if a normal array was passed in. In a multidimensional array, the second-level arrays and beyond can be different sizes.

An ArrayList can change size over its life. It can be stored in an ArrayList or List reference.

Generics can specify the type that goes in the ArrayList. Although an ArrayList is not allowed to contain primitives, Java will autobox parameters passed in to the proper wrapper type. Collections.sort() sorts an ArrayList.

Exam Essentials

Be able to determine the output of code using String. Know the rules for concatenating Strings and how to use common String methods. Know that Strings are immutable. Pay special attention to the fact that indexes are zero-based and that substring() gets the string up until right before the index of the second parameter.

Be able to determine the output of code using StringBuilder. Know that StringBuilder is mutable and how to use common StringBuilder methods. Know that substring() does not change the value of a StringBuilder, whereas append(), delete(), and insert() do change it. Also note that most StringBuilder methods return a reference to the current instance of StringBuilder.

Understand the difference between == and equals(). == checks object equality. equals() depends on the implementation of the object it is being called on. For Strings, equals() checks the characters inside of it.

Be able to determine the output of code using arrays. Know how to declare and instantiate one-dimensional and multidimensional arrays. Be able to access each element and know when an index is out of bounds. Recognize correct and incorrect output when searching and sorting.

Be able to determine the output of code using ArrayList. Know that ArrayList can increase in size. Be able to identify the different ways of declaring and instantiating an ArrayList. Identify correct output from ArrayList methods, including the impact of autoboxing.