Array Covariance and Arrays to Stream


Array Covariance

Object arrays are covariant, which means that just as Integer is a subclass of Number, Integer[] is a subclass of Number[]. This may seem intuitive, but can result in surprising behavior:

Integer[] integerArray = {1, 2, 3};
Number[] numberArray = integerArray; // valid
Number firstElement = numberArray[0]; // valid
numberArray[0] = 4L; // throws ArrayStoreException at runtime

Although Integer[] is a subclass of Number[], it can only hold Integers, and trying to assign a Long element throws a runtime exception.

Note that this behavior is unique to arrays, and can be avoided by using a generic List instead:

List<Integer> integerList = Arrays.asList(1, 2, 3);
//List<Number> numberList = integerList; // compile error
List<? extends Number> numberList = integerList;
Number firstElement = numberList.get(0);
//numberList.set(0, 4L); // compile error

It's not necessary for all of the array elements to share the same type, as long as they are a subclass of the array's type:

interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
I[] array10 = new I[] { new A(), new B(), new C() }; // Create an array with new
                                                     // operator and array initializer.
 
I[] array11 = { new A(), new B(), new C() }; // Shortcut syntax with array
                                                     // initializer.
I[] array12 = new I[3]; // { null, null, null }
 
I[] array13 = new A[] { new A(), new A() }; // Works because A implements I.
 
Object[] array14 = new Object[] { "Hello, World!", 3.14159, 42 }; // Create an array with
                                                                  // new operator and array initializer.
 
Object[] array15 = { new A(), 64, "My String" }; // Shortcut syntax
                                                 // with array initializer.

Arrays to Stream

Converting an array of objects to Stream:

String[] arr = new String[] {"apple", "banana", "cherry"};
Stream<String> stream = Arrays.stream(arr);

Converting an array of primitives to Stream using Arrays.stream() will transform the array to a primitive specialization of Stream:

int[] intArr = {1, 2, 3};
IntStream intStream = Arrays.stream(intArr);

You can also limit the Stream to a range of elements in the array. The start index is inclusive and the end index is exclusive:

int[] values = {1, 2, 3, 4};
IntStream intStream = Arrays.stream(values, 2, 4);

A method similar to Arrays.stream() appears in the Stream class: Stream.of(). The difference is that Stream.of() uses a varargs parameter, so you can write something like:

Stream<Integer> intStream = Stream.of(1, 2, 3);
Stream<String> stringStream = Stream.of("1", "2", "3");
Stream<Double> doubleStream = Stream.of(new Double[]{1.0, 2.0});

Arrays to a String

Since Java 1.5 you can get a String representation of the contents of the specified array without iterating over its every element. Just use Arrays.toString(Object[]) or Arrays.deepToString(Object[]) for multidimentional arrays:

int[] arr = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]
int[][] arr = {
   {1, 2, 3},
   {4, 5, 6},
   {7, 8, 9}
};
System.out.println(Arrays.deepToString(arr)); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Arrays.toString() method uses Object.toString() method to produce String values of every item in the array, beside primitive type array, it can be used for all type of arrays. For instance:

public class Cat { /* implicitly extends Object */
  @Override
  public String toString() {
     return "CAT!";
  }
}
Cat[] arr = { new Cat(), new Cat() };
System.out.println(Arrays.toString(arr)); // [CAT!, CAT!]

If no overridden toString() exists for the class, then the inherited toString() from Object will be used. Usually the output is then not very useful, for example:

public class Dog {
  /* implicitly extends Object */
}
Dog[] arr = { new Dog() };
System.out.println(Arrays.toString(arr));
 

Basic Programs