Java 11 Developer Certification - Array Manipulation

October 30, 2020

What we are covering in this lesson

  1. Arrays class
  2. Search and comparision methods
  3. Data Manipulation methods
  4. Data Transformation methods
  5. Array search methods

Arrays class

Javadoc definition of java.util.Arrays class states it contains various methods for manipulating arrays (such as sorting and searching). This class also contains a static factory that allows arrays to be viewed as lists. Arrays class has many methods which can help us manipulate data in the array. Usually when we have to work with arrays, we often end up using some of the methods of Arrays class in order to save time. We will look at some of these methods in this section.

We will have a look at some of the methods which can be used from this Arrays class.

Search and comparision methods

The Arrays class comparision methods are

  • compare (introduced in Java 9)
  • compareUnsigned (introduced in Java 9)
  • deepEquals
  • equals

Method for search are

  • binarySearch
  • matches (introduced in Java 9)

Data Manipulation methods

The method for data manipulation in an array are mentioned below

  • deepHashCode
  • deepToString
  • fill
  • hashCode
  • parallelPrefix (introduced in Java 8)
  • parallelSort (introduced in Java 8)
  • parallelSetAll (introduced in Java 8)
  • setAll (introduced in Java 8)
  • sort
  • toString

Data Transformation methods

Below methods are used for data transformation in an array

  • asList
  • copyOf
  • copyOfRange
  • spliterator (introduced in Java 8)
  • stream (introduced in Java 8)

Arrays.equals method looks at the elements in the array (and the order as well) to determine if the arrays are equal. Object.equals method checks the reference and only objects with same reference are considered equal.

The Arrays.compare method uses prefix which is the common set of elements starting at element index 0, so if we have teo arrays with same set of elements, comparing them will return a prefix equal to the entire set of elements. In short, the common elements set form the prefix. compare method also follow the below rules

  • If Arrays.equals is true, return 0
  • If first array passed to parameter is null, return -1, else if second array is null, return 1
  • If length of first array is 0, return (0-length of second array)
  • If length of second array is 0, return (length of first array-0)
  • If one array represents the entire prefix of another, the return value is the difference in length of the array, which will be negative if we compare smaller array to a larger array
  • If no prefix is identified, then the first element of each array is compared lexicographically, which will ne negative if first element of first array is smaller than first element of second array
  • If a prefix is identified, but neither array represents a full subset of the other, the index where the prefix stops is used to compare the elements at that index

The same can be easily visualized by the below table

Array a Array b Arrays.compare(a,b) Notes
a = {el1,el2} b = {el1,el2} 0 arrays equal using Arrays.equals(a,b)
a = null b = {el1,el2} -1 since first array is null
a = {el1,el2} b = null 1 since second array is null
a = {} b = {el1,el2} a.length-b.length a.length == 0
a = {el1,el2} b = {} a.length-b.length b.length == 0
a = {el1,el2} b = {el1,el2,el3} a.length-b.length prefix == {el1,el2}
a = {el1,el2} b = {el3,el4} a[0].compareTo(b[0]) no prefix identified
a = {el1,el2,el3} b = {el1,el2,el4} a[2].compareTo(b[2]) prefix is {el1,el2} but its not complete array set so first element difference

Now lets go through some code to look at some of the methods and their usage.

package maxCode.online.Arrays;

import java.util.Arrays;
import java.util.List;

public class ArrayManipulation {
	public static void main(String[] args) {
		// Set up some array data for comparison.
        String[] firstString = {"abc", "def", "ghi", "jkl", "mno",
                "pqr", "stu", "vwx", "yz"};
        String[] copyOfFirstString = {"abc", "def", "ghi", "jkl", "mno",
                "pqr", "stu", "vwx", "yz"};
        String[] firstStringUnsorted = {"jkl", "mno", "pqr", "stu", "vwx",
                "yz", "abc", "def", "ghi"};
        String[] partialFirstString = {"abc", "def", "ghi", "jkl", "mno"};

        String firstStringReference[] = firstString;

        // Create pass-thru Lists from the arrays to test List equality
        List firstList = Arrays.asList(firstString);
        List secondList = Arrays.asList(copyOfFirstString);

        // First, compare arrays using object.equals
        System.out.println("------------- Object.equals ------------");
        System.out.println(" (firstString.equals(copyOfFirstString)) = "
                + firstString.equals(copyOfFirstString));
        System.out.println(" (firstString.equals(firstStringReference)) = "
                + firstString.equals(firstStringReference));

        // Compare arrays using Arrays.equals
        System.out.println("\n------------- Arrays.equals ------------");
        System.out.println(" (Arrays.equals(firstString,copyOfFirstString)) = "
                + Arrays.equals(firstString, copyOfFirstString));
        System.out.println(" (Arrays.equals(firstString,firstStringReference)) = "
                + Arrays.equals(firstString, firstStringReference));
        System.out.println(" (Arrays.equals(firstString,firstStringUnsorted)) = "
                + Arrays.equals(firstString, firstStringUnsorted));

        // Compare arrays using Arrays.compare
        System.out.println("\n------------- Arrays.compare ------------");
        System.out.println(" (Arrays.compare(firstString,copyOfFirstString)) = "
                + Arrays.compare(firstString, copyOfFirstString));
        System.out.println(" (Arrays.compare(firstString,firstStringReference)) = " +
                "" + Arrays.compare(firstString, firstStringReference));
        System.out.println(" (Arrays.compare(firstString,firstStringUnsorted)) = "
                + Arrays.compare(firstString, firstStringUnsorted));
        System.out.println(" (Arrays.compare(firstStringUnsorted,firstString)) = "
                + Arrays.compare(firstStringUnsorted, firstString));
        System.out.println(" (Arrays.compare(firstString,partialFirstString)) = "
                + Arrays.compare(firstString, partialFirstString));
        System.out.println(" (Arrays.compare(partialFirstString,firstString)) = "
                + Arrays.compare(partialFirstString, firstString));


        // More examples of Arrays.compare
        System.out.println("\n------------- More Arrays.compare ------------");
        System.out.println(" (Arrays.compare(firstString,firstStringUnsorted)) = "
                + Arrays.compare(firstString, firstStringUnsorted));

        // If we compare the first elements, we get the same result as comparing the full arrays...
        System.out.println("firstString[0].compareTo(firstStringUnsorted[0]  = "
                + firstString[0].compareTo(firstStringUnsorted[0]));

        //  New String array, only  first 3 elements are shared with firstString - the prefix.
        String[] notFullyPrefixedString = {"abc", "def", "ghi", "yz", "vwx", "stu"};
        System.out.println(" (Arrays.compare(firstString,notFullyPrefixedString)) = "
                + Arrays.compare(firstString, notFullyPrefixedString));

        // Verify that above result is same as comparing elements at  first non-matching index..
        System.out.println("firstString[3].compareTo(notFullyPrefixedString[3]  = " +
                firstString[3].compareTo(notFullyPrefixedString[3]));


        // Comparing subsets of elements in arrays
        System.out.println("\n----  Arrays.compare for Element Ranges ----");

        // Set up arrays - note that both arrays contain "abc", "def", "ghi"
        String[] stringArray = {"abc", "def", "ghi", "jkl", "mno",
                "pqr", "stu", "vwx", "yz"};

        String[] stringUnsortedArray = {"jkl", "mno", "pqr", "stu",
                "vwx", "yz", "abc", "def", "ghi"};

        // We are going to compare the 1st 3 elements of the ordered array,
        // with the last 3 elements of the unsorted array:
        System.out.println(" (Arrays.compare(stringArray, 0, 3, stringUnsortedArray, 6, 9)) = "
                + Arrays.compare(stringArray, 0, 3, stringUnsortedArray, 6, 9));

        // Interestingly, you do not have to specify the same number of elements.
        System.out.println(" (Arrays.compare(stringArray, 0, 5, stringUnsortedArray, 6, 9)) = "
                + Arrays.compare(stringArray, 0, 5, stringUnsortedArray, 6, 9));

        System.out.println("\n------------- List.equals ------------");
        System.out.println(" (firstList.equals(secondList)) = "
                + firstList.equals(secondList));
	}
}

In the first part, we are setting up some test data, converting array to list and comparing the arrays using Object equals method. As mentioned earlier, Object equals method returns true only when the references to both the arrays are the same. Arrays with same set of data but different references will not be considered equal. This is why we get the below output for the first part.

------------- Object.equals ------------
 (firstString.equals(copyOfFirstString)) = false
 (firstString.equals(firstStringReference)) = true

Now in the second part, we use Arrays.equals method instead of Object equals. It looks at the array elements to determine if the arrays are equal, also checks the order of the elements and so the last output is false.

------------- Arrays.equals ------------
 (Arrays.equals(firstString,copyOfFirstString)) = true
 (Arrays.equals(firstString,firstStringReference)) = true
 (Arrays.equals(firstString,firstStringUnsorted)) = false

Then we have the Arrays.compare method, and we have already covered that before. Look at the table and the output can be easily understood

------------- Arrays.compare ------------
 (Arrays.compare(firstString,copyOfFirstString)) = 0
 (Arrays.compare(firstString,firstStringReference)) = 0
 (Arrays.compare(firstString,firstStringUnsorted)) = -9
 (Arrays.compare(firstStringUnsorted,firstString)) = 9
 (Arrays.compare(firstString,partialFirstString)) = 4
 (Arrays.compare(partialFirstString,firstString)) = -4

------------- More Arrays.compare ------------
 (Arrays.compare(firstString,firstStringUnsorted)) = -9
firstString[0].compareTo(firstStringUnsorted[0]  = -9
 (Arrays.compare(firstString,notFullyPrefixedString)) = -15
firstString[3].compareTo(notFullyPrefixedString[3]  = -15

----  Arrays.compare for Element Ranges ----
 (Arrays.compare(stringArray, 0, 3, stringUnsortedArray, 6, 9)) = 0
 (Arrays.compare(stringArray, 0, 5, stringUnsortedArray, 6, 9)) = 2

Array Seach methods

There are a few methods using which we can search for elements within the array. We will have a look at the widely used Arrays.binarySearch and a few more with it. The binarySearch method searches for a matching element and returns an integer indicating the array index where there is a match.

  • If no match is found, the method returns a -1.
  • When using a binarySearch, your array needs to be sorted.
  • You can also perform a binarySearch on a non-sorted array but the results cannot be relied on.
  • If your array contains duplicate values there’s no guarantee which index of the duplicated elements will be returned.

So as you can understand, the primary requirement to get correct result in case of binary search is that the array must be in sorted order. Otherwise, output will be provided but that cannot be relied on.

Lets look at a small code to understand the search methods properly.

package maxCode.online.Arrays;

import java.util.Arrays;
import java.util.List;

public class ArraySearch {
	public static void main(String[] args) {
		// Set up some array data we want to compare.
        String[] firstString = {
                "abc", "def", "ghi", "jkl", "mno",
                "pqr", "stu", "vwx", "yz"
        };
        String[] firstStringUnsortedDuplicates = {
                "jkl", "mno", "pqr", "stu", "vwx",
                "yz", "jkl", "abc", "def", "ghi"
        };
        String[] partialFirstString = {"abc", "def", "ghi", "jkl", "mno"};

        // Create Lists from the arrays to test List search methods
        List firstList = List.of(firstString);
        List secondList = Arrays.asList(firstStringUnsortedDuplicates);

        System.out.println("---------- Arrays binarySearch  ----------");
        // binary search on array, look for "jkl" which is in array
        String searchString = "jkl";
        System.out.println("Arrays.binarySearch(firstString,\"jkl\") = "
                + Arrays.binarySearch(firstString, "jkl"));

        // binary search, look for "aaa",  which is not in array
        System.out.println("Arrays.binarySearch(firstString,\"aaa\") = "
                + Arrays.binarySearch(firstString, "aaa"));

        //  binary search, look for "jkl" of which there are two elements
        System.out.println("Arrays.binarySearch(firstStringUnsortedDuplicates,\"jkl\") = "
                + Arrays.binarySearch(firstStringUnsortedDuplicates, "jkl"));

        //  binary search on unsorted array, for "abc" which is in array
        System.out.println("Arrays.binarySearch(firstStringUnsortedDuplicates,\"abc\") = "
                + Arrays.binarySearch(firstStringUnsortedDuplicates, "abc"));

        System.out.println("\n---------- Arrays mismatch  ----------");
        // mismatch returns the non-matching index where the prefix ends
        System.out.println("Arrays.mismatch(firstString,partialFirstString) = "
                + Arrays.mismatch(firstString, partialFirstString));

        // Try another example...
        System.out.println("Arrays.mismatch(firstStringUnsortedDuplicates," +
                " new String[]{\"jkl\",\"mno\"}) = "
                + Arrays.mismatch(firstStringUnsortedDuplicates, new String[]{"jkl", "mno"}));

        System.out.println("\n-------------- List methods  -------------------");
        // Does array contain "def"?
        System.out.println("firstList.contains(\"def\") =  "
                + firstList.contains("def"));

        // Does array contain elements in partialString?
        System.out.println("firstList.containsAll(Arrays.asList(partialFirstString)) =  "
                + firstList.containsAll(Arrays.asList(partialFirstString)));

        // create a second list not in same order
        String[] anotherUnsortedSet = {"jkl", "def", "abc", "ghi", "mno"};
        System.out.println("firstList.containsAll(Arrays.asList(anotherUnsortedSet)) =  "
                + firstList.containsAll(Arrays.asList(anotherUnsortedSet)));

        // Use indexOf to get first matching element
        System.out.println("secondList.indexOf(\"jkl\") =  "
                + secondList.indexOf("jkl"));

        // Use lastIndexOf to to get last matching element
        System.out.println("secondList.lastIndexOf(\"jkl\") =  "
                + secondList.lastIndexOf("jkl"));
    }
}

Output

---------- Arrays binarySearch  ----------
Arrays.binarySearch(firstString,"jkl") = 3
Arrays.binarySearch(firstString,"aaa") = -1
Arrays.binarySearch(firstStringUnsortedDuplicates,"jkl") = 0
Arrays.binarySearch(firstStringUnsortedDuplicates,"abc") = -1

---------- Arrays mismatch  ----------
Arrays.mismatch(firstString,partialFirstString) = 5
Arrays.mismatch(firstStringUnsortedDuplicates, new String[]{"jkl", "mno"}) = 2

-------------- List methods  -------------------
firstList.contains("def") =  true
firstList.containsAll(Arrays.asList(partialFirstString)) =  true
firstList.containsAll(Arrays.asList(anotherUnsortedSet)) =  true
secondList.indexOf("jkl") =  0
secondList.lastIndexOf("jkl") =  6

The List.of method was introduced in Java 9 and the basic difference between Arrays.asList and List.of are as below

  • List.of returns an immutable list whereas Arrays.asList returns a mutable list. So you cannot do a list.set in the list returned by List.of
  • Arrays.asList returns a view to the underlying array, so any changes in the array elements will also reflect in the list values. But this is not the case in list returned by List.of
  • List.of doesnt allow null values whereas Arrays.asList allows null values.

First we are doing a binary search on the array for “jkl”, then for “aaa” which doesnt exist in the array and hence return value is -1. The next output for search on “jkl” does provide correct result, but as per the javadoc the search on unsorted array is not to be relied on, as is clearly seen in the next output.

Now, as we know about array prefix, Arrays.mismatch returns the index where the prefix no longer applies. So the output for Arrays.mismatch are straight forward.

And the final set has some List search methods. contains and containsAll are used to see if an element or a list of elements is contained in the list. These methods return true or false and the containsALL method does not require the elements to be in the same order to return true.

The indexOf and lastIndexOf returns the first or last index of the element matching in the array.

Thats all in this section. We will look into the Array Search methods in the next section.