JavaOCP/Collections/Collections/src/main/java/ocp/collections/Arrays/App.java

314 lines
9.4 KiB
Java

package ocp.collections.Arrays;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Here we play with Java arrays and the Java Collections API
*
*/
public class App
{
public static void main( String[] args )
{
/**
* Arrays in java are pretty easy to work with
*/
String[] myArray = new String[] {"Alice", "Bob"};
System.out.println(Arrays.toString(myArray)); // [Alice, Bob]
/**
* Any array is an object too, hence we can store
* it in the <code>Object</code> super-type
*/
Object thing = myArray;
/**
* You can also cast the array <code>T[]</code>
* to <code>A[]</code> so long as T is a kind-of
* A (which is true as T=String, and A=CharSequence)
* and String is a kind-of CharSequence
*/
CharSequence[] thing2 = myArray;
thing2[0] = "Eve";
System.out.println(Arrays.toString(myArray)); // [Eve, Bob]
/**
* Remember the object's actual type is
* <code>String[]</code>, so we must check
* when we downcast
*/
if(thing2 instanceof String[])
{
String[] myCastedArray = (String[])thing2;
myCastedArray[1] = "Peter";
System.out.println(Arrays.toString(myCastedArray)); // [Eve, Peter]
}
/**
* We can also do it via <code>thing</code>
*/
if(thing instanceof CharSequence[])
{
CharSequence[] myCastedArray = (CharSequence[])thing;
myCastedArray[1] = "John";
System.out.println(Arrays.toString(myCastedArray)); // [Eve, John]
}
/**
* We can also do it via <code>thing</code>
*/
if(thing instanceof String[])
{
String[] myCastedArray = (String[])thing;
myCastedArray[0] = "Nick";
System.out.println(Arrays.toString(myCastedArray)); // [Nick, John]
}
/**
* The Arrays class has a bunch of helper methods that we can
* use to help us working with arrays
*
* The binarySearch does a binary search on our array
*/
int[] numbers = {5,4,1,2,1};
int pos = Arrays.binarySearch(numbers, 0);
System.out.println("Position: "+pos); // -1 because not found
pos = Arrays.binarySearch(numbers, 1);
System.out.println("Position: "+pos); // non-negative because found
/**
* Apply in-place sorting
*/
System.out.println("Before sorting: "+Arrays.toString(numbers));
Arrays.sort(numbers);
System.out.println("After sorting: "+Arrays.toString(numbers));
/**
* You can make copies of arrays using copyof
* which takes in the original array, the
* length to copy and then a Class object
* to specify the new array to be made
*/
Object[] bruh = new Object[] {new Object(), new Object()};
Object[] bruhCopy = Arrays.copyOf(bruh, 2, bruh.getClass());
System.out.println(bruh == bruhCopy); // false
/**
* The equals method will look at two arrays
* and if the elements are objects, check equality
* between them.
*
* However an array of arrays, means that the references
* will be checked on the inner array (as they are outer
* array elements).
*
* For a full equality we use deepEquals
*
*/
Arrays.deepEquals(bruh, bruhCopy);
Arrays.equals(bruh, bruhCopy);
/**
* It should be noted that this will
* give us a {@link ClassCastException}.
*
* Remember the object's ACTUAL type
* is that of a <code>Object[]</code>
* and casting can only be to <code>Object</code>
* (as all arrays are Objects) OR to
* a compatible type according to
* the T[] to S[] where T is a kind-of S
*
* But if T=Object and S=String, well,
* T is not a kind of S.
*
* Object is not a kind-of String
*/
Object[] arr2 = new Object[] {"Hello", "World"};
try
{
String[] arr2Casted = (String[])arr2;
}
catch(ClassCastException e)
{
System.out.println("Sorry but got: '"+e+"'");
}
/**
* We create a custom array list
*/
ArrayList<String> strList = new ArrayList<String>();
strList.add("Hello");
/**
* Compiler does static type checking of
* anything with parameterized type `T`
* being `Object`
*/
ArrayList<?> l = strList;
ArrayList<Integer> intList = (ArrayList<Integer>)l;
/**
* The below work fine of course, all they
* are doing is calling the methods.
*
* There is no compile-time to-type request
* in this code that the static type checker
* would need to enforce
*/
intList.size();
intList.get(0);
intList.add(2);
/**
* As soon as we assign then we have a
* runtime type type occur of the
* to-type (Integer (from variable))
* and the actual type of the object
* returned from `get(0)`.
*
* Compile-time wise, it checks out
* as the `T` type it `Integer`
* and the to-type (of the variable)
* is `Integer.
*
* So it is generalized probably to
* check the object's runtime type
* against the paramaterized type `T`
* as compile-time guaranteed we had
* to-type (of variable) compatible with
* the parameterized type `T` and therefore
* we need check the runtime type to
* `T`.
*
* So yes because the objects actual
* type of `String` and `String`
* is not compatible type-wise with
* `T` (which is `Integer`) we then
* get a `ClassCastException`.
*
* By the way this phenomenom
* is known as HEAP POLUTTION!
*
* (https://en.wikipedia.org/wiki/Heap_pollution)
*/
// Integer myInt = intList.get(0);
/**
* This will work because the actual
* type of the Object at index 1 is
* an Integer or Integer-compatible
* object (in this case exactly an
* Integer).
*
* Therefore the actual runtime type
* matches the to-type which is the
* type of the variable being assigned
* t.
*/
Integer myIntFr = intList.get(1);
/**
* Var-args will create an array
* who's actual type is determined
* at compile-time by the lowest common
* denominator of compatible types
* across all the types of the provided
* arguments
*/
thing(1,1); //Integer[] actual type
thing("Hello", 1); //Serializable[] actual type
thing("Hello", "World"); //String[] actual type
thing("Hello", new Object()); //Object[] actual type
System.out.println();
/**
* We call `getArray(T...)` with certain
* arguments and the most common type will
* be used as the kind-of array to construct.
*
* Here we then expect it to be a `String[]`
*
* This follows from the exact same logic
* as calling `thing(T...)` above.
*/
Object retObj1 = getArray("hello");
System.out.println(retObj1.getClass()); // String[]
/**
* See the definition of `get()` for
* an explanation
*/
Object retObj = get("Hello");
System.out.println(retObj.getClass()); //Object[]
}
private static <E> void thing(E... items)
{
System.out.println(items.getClass());
}
@SafeVarargs
private static <T> T[] getArray(T... items)
{
return items;
}
private static <T> T[] get(T item)
{
/**
* The problem here is that if we call
* a var-args method with a parameterized
* ARGUMENT (the `item` here) then it
* will always seem to choose the common
* type as `Object` therefore making the
* array passed in an `Object[]` and of course
* that, in this case,is immediately returned
* to us.
*
* Not sure why it's like this in Java, perhaps
* they don't explore all the calls but they
* probably only do template code generation
* based on like a first level call and
* maybe not based off of calls-to-calls
* (which D definitely does)
*
* From the docs (https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html):
*
* "When the compiler encounters a varargs method,
* it translates the varargs formal parameter into an array.
* However, the Java programming language does not permit the
* creation of arrays of parameterized types."
*
* Yeah, so it's the same thing, cannot
* construct an array of a parameterized type
* T which is exactly what `item` IS!
*
* Compared to the inner call, `getArray`
* when used by itself is a concrete type
*
* We can use @SafeVarargs to get the compiler
* warnings to shut up if we know exactly what
* it is that we are doing as in the case of
* `multipleItems_weKnowWhatWeAreDoingVersion`
* as we know its going to be an `Object[]`
* but it would for any call using parameterized
* arguments, hence if we know for sure then
* we get compile rwarnings supressed
*/
// T[] gg = new T[];
Object[] multipleItems_weKnowWhatWeAreDoingVersion = getArray(item, item, item);
T[] multipleItems = getArray(item, item, item);
return multipleItems;
}
}