| hashCode and equals |
|---|
|
If you want to compare two objects meaningfully, you need to override the equals() method.
Without overriding it, equals() defaults to using == operator, which checks whether two
variables point to the same object.
If you don't override an equals() method for your class, you won't be able to use instances of that class as keys in a hashtable, and your Sets will be messed up. Syntax for overriding the equals method:
public boolean equals(Object o)
You must keep it public, and it must accept a single Object as an argument, which is the object to be compared.
|
If you override the equals method, you should override the hashCode method as well.
The rules are:
However, it IS permitted to check one field only on the hashCode routine, and check 27 different fields on the equals routine. Don't use transient variables for either the hashCode or the equals test!
|
| Collections |
|---|
| If you want to use Collections or Lists, you will have to import java.util.* first. |
| Collections can hold objects, but not primitives. If you insert a primitive into a collection, it is actually autoboxed into a wrapper object. |
|
Here is the interface hierarchy you should know:
|
| Collection Implementation Classes | |||||
|---|---|---|---|---|---|
| Class | Map | Set | List | Ordered | Sorted |
| Hashtable | x | No | No | ||
| HashMap | x | No | No | ||
| TreeMap | x | Sorted | By natural order or comparator | ||
| LinkedHashMap | x | By insertion order | No | ||
| HashSet | x | No | No | ||
| TreeSet | x | Sorted | By natural order or comparator | ||
| LinkedHashSet | x | By insertion order | No | ||
| ArrayList | x | By index | No | ||
| Vector | x | By index | No | ||
| LinkedList | x | By index | No | ||
| PriorityQueue | Sorted | By to-do order | |||
| Sorting Collections and Arrays |
|---|
To sort a collection, you need to either:
-1 if this < obj2 0 if this == obj2 1 if this > obj2 The compare routine on a Comparable object is passed two objects to compare, and
should return:
An easy way to remember what value it should return, is this: If they were numbers, you'd be returning num1 - num2. When you want to sort an array or collection, the items must all be mutually comparable. In other words, if you have an array of Object and you put ChristmasTree and Integer objects into it, you won't be able to sort it. Depending on the implementation, you may either get no error or a RuntimeError. |
| Searching Collections and Arrays |
|---|
|
Searches are performed using the static methods Collections.binarySearch() or Arrays.binarySearch() method.
Before calling this routine, first make sure your collection or array is sorted! If you used a comparator, be sure and pass the comparator to the binarySearch() method, or you will get unexpected results. |
An example illustrating searching and sorting using custom comparators follows:
import java.util.*;
public class testBinarySearch {
public static void main(String[] args) {
// Create a list
Integer[] arr = new Integer[10];
for (int i = 0; i < 10; i++)
arr[i] = i;
// Sort it
Arrays.sort(arr);
// Search it
int index = Arrays.binarySearch(arr, 2);
System.out.println(Arrays.toString(arr));
System.out.printf("Item found at index %d - value was %d.\n",
index, arr[index]);
// Use a custom comparator
ReverseComparator rs = new ReverseComparator<Integer>();
Arrays.sort(arr, rs);
//must pass comparator since arr was sorted with it
index = Arrays.binarySearch(arr, 2, rs);
System.out.println(Arrays.toString(arr));
System.out.printf("Item found at index %d - value was %d.\n",
index, arr[index]);
}
}
class ReverseComparator<T extends Comparable> implements Comparator<T> {
public int compare(T obj1, T obj2) {
return obj2.compareTo(obj1);
}
}
|
| Array and Collection Misc. |
|---|
|
The List and Set classes have "toArray() methods that convert the collection to a normal array.
The Arrays class has a method called "asList" that copies the items into a List. When you use this, the array and list are joined - when you change one of them, the other gets updated automatically. Though this seems handy, it's not so much - you can't modify the size of the created list, ie, remove items or add items. The following program illustrates the problem.
import java.util.*;
public class testAsList {
public static void main(String[] args) {
// Arrays.asList demo
String[] sa = {"one", "two", "buckle", "my", "shoe"};
List lst = Arrays.asList(sa);
System.out.println(Arrays.toString(sa));
System.out.println(lst.toString());
try {
lst.add("three");
} catch (Exception ex) {
ex.printStackTrace();
}
// can't enlarge arrays.
// sa.length = sa.length + 1;
}
}
|
|
ArrayLists are an ordered, unsorted implementation of List. Though usually accessed by index,
you can also access it by iterator.
To create an iterator, use the syntax:
Iterator i = lst.iterator();
while (i.hasNext()) {
Dog obj = (Dog)i.next();
// Do something nice with obj
}
Or the generic form:
Iterator<Cat> i = lst.iterator();
while (i.hasNext()) {
Cat cat = i.next();
// Do something nice with cat
}
|
TreeSets are a set whose elements are sorted.
If you insert duplicate items into a treeset, you'll get a RuntimeError.
The items in a TreeSet should be mutually comparable - if they're not, you'll get an error at runtime.
lower - return the highest element < e
higher - return the lowest element > e
floor - return the highest element <= e
ceiling - return the lowest element >= e
Items with no suffix, such as ceiling, returns a value
Items with a suffix 'Key', such as ceilingKey, returns a key
Items with a suffix 'Entry', such as ceilingEntry, returns a key
+ value (Map.Entry)
Polling both retrieves AND removes items. |
Backed collections are collections that are subsets of the original collection.
Changes made in one, are reflected in the other, including adding and removing.
Backed collections are created with the following routines:
// Return a subset starting at element e
// A set is a value only collection (no keys)
headSet(e, inclusive)
tailSet(e, inclusive)
subSet (s, fromInclusive, e, toInclusive)
// Return a submap starting at element e
// A map is a key/value collection
headMap(k, inclusive)
tailMap(k, inclusive)
subMap (s, fromInclusive, e, toInclusive)
If a boolean 'inclusive' argument exists, the result is a NavigableSet or NavigableMap.
If no boolean argument exists, the result is a SortedSet or a SortedMap. An example follows:
import java.util.*;
public class testTailMap {
public static void main(String... args) {
TreeSet ts = new TreeSet();
for (int i = 65; i < 65 + 26; i++)
ts.add((char)i);
System.out.println(ts.toString());
System.out.println("");
// headSet defaults to inclusive = false
// Return a subset starting at element E and exclusive of E
System.out.println(ts.headSet('E').toString());
// Return a subset starting at element E and exclusive of E
System.out.println(ts.headSet('E', false).toString());
// Return a subset starting at element E and inclusive of E
System.out.println(ts.headSet('E', true).toString());
System.out.println("");
// tailSet defaults to inclusive = true
// Return a subset starting at key k and inclusive of k
System.out.println(ts.tailSet('W').toString());
// Return a subset starting at key k and inclusive of k
System.out.println(ts.tailSet('W', true).toString());
// Return a subset starting at key k and exclusive of k
System.out.println(ts.tailSet('W', false).toString());
System.out.println("");
// subSet defaults to fromInclusive = true, toInclusive = false
// Return a subset starting at element start and ending at end - 1
System.out.println(ts.subSet('A', 'C').toString());
// Return a subset starting at element start and ending at end - 1
System.out.println(ts.subSet('A', true, 'C', false).toString());
// Return a subset starting at element start and ending at end
System.out.println(ts.subSet('A', true, 'C', true).toString());
// Return a subset starting at element start + 1 and ending at end
System.out.println(ts.subSet('A', false, 'C', true).toString());
// Return a subset starting at element start + 1 and ending at end - 1
System.out.println(ts.subSet('A', false, 'C', false).toString());
System.out.println("");
/*
The above program prints out:
[A, B, C, D, E, F, G, H, I, J, K, L, M, N,
O, P, Q, R, S, T, U, V, W, X, Y, Z]
[A, B, C, D]
[A, B, C, D]
[A, B, C, D, E]
[W, X, Y, Z]
[W, X, Y, Z]
[X, Y, Z]
[A, B]
[A, B]
[A, B, C]
[B, C]
[B]
*/
TreeMap tm = new TreeMap();
tm.put("b", "Bear");
tm.put("c", "Cat");
tm.put("d", "Dog");
tm.put("e", "Emu");
tm.put("f", "Frog");
SortedMap sm = tm.subMap("b", true, "c", true);
System.out.println(tm.toString());
System.out.println(sm.toString());
// {b=Bear, c=Cat, d=Dog, e=Emu, f=Frog}
// {b=Bear, c=Cat}
tm.put("bee", "Bee");
System.out.println(tm.toString());
System.out.println(sm.toString());
// {b=Bear, bee=Bee, c=Cat, d=Dog, e=Emu, f=Frog}
// {b=Bear, bee=Bee, c=Cat}
sm.put("x", "exception"); // RuntimeException "key out of range"
}
}
|
|
HashMaps and Hashtables use put, not add!
Internally, when the JVM is looking for an item in a hashtable, it uses the hashCode() of the object to find the correct bucket. After finding the correct bucket, it searches all of them with equals() to find the object in the bucket. |
| Generics |
|---|
|
Generics can be used in many places, but most programmers use them only to create type-safe collections.
Generics provide compile-time assistance ONLY! At run time, type erasure gets rid of the generic types, and everything is manipulated as an Object again. So don't go using generics with the notion that it will help ensure run-time safety, or anything. If your code would be simpler or faster without generics, don't use it. Problems can arise when mixing older non-generic code with newer code that uses generics - the compiler will warn you if it detects this. It can happen when you have a newer type-safe generic list, and pass it to older code, which could stuff anything in there - Integers, Strings, fish, hatchets. To make non-generic code generic, add angle brackets after the collection type in both the variable declaration and the constructor call, including any place you declare a variable, like arguments and return types. An older List that held objects
List list = new ArrayList();
turns into this, to make it hold only Integers
List<Integer> list = new ArrayList<Integer>();
Here's an example:
import java.util.*;
public class testGenerics {
public static void main(String[] args) {
testArrayList();
testArrayListWithGenerics();
}
public static void testArrayList() {
ArrayList arr = new ArrayList();
for (int i = 0; i < 10; i++)
arr.add(i);
System.out.println(arr.toString());
// Prints [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// This will work just fine.
Double d = 54.2;
arr.add(d);
// Cast required to get item out of array
Integer foundit = (Integer) arr.get(2);
System.out.println(foundit);
// Prints 2
// Throws error
//for (Object obj : arr) {
// Will throw RuntimeException IllegalFormatConversionException
// when it hits the double value 54.2!
// That's the problem without using generics.
// System.out.printf("%d ", obj);
//}
}
public static void testArrayListWithGenerics() {
ArrayList<Integer> arr = new ArrayList<Integer>();
for (int i = 0; i < 10; i++)
arr.add(i);
System.out.println(arr.toString());
// Prints [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// Compile time error - can't add double to Integer list
Double d = 54.2;
// arr.add(d); <-- can't do this
// No cast required to get item out of array.
Integer foundit = arr.get(2);
System.out.println(foundit);
// Prints 2
// Prints 0 1 2 3 4 5 6 7 8 9
for (Object obj : arr) {
// Will no longer throw RuntimeException
// because double value 54.2 was not stuffed in there.
// Generics fixed the problem.
System.out.printf("%d ", obj);
}
}
}
|
|
Polymorphism doesn't apply to the types stuffed in the angle brackets, with generics.
If the variable says List<Integer> myList, it can only be assigned to an ArrayList<Integer>,
not an ArrayList<Object>. The stuff in the angle brackets has to match! Or be missing altogether,
such as when passing a ArrayList<Integer> to a method that requires an List.
Remember, we're talking about generics here. Without generics, an array of Object can be pointed to an array of Animal, no problem, since Animal inherits from Object. With generics, you can't do this. In short, this is valid:
Object[] myArray = new JButton[4]; // OK, doesn't use generics
much in the way this, is not:
List<Object> myArray = new ArrayList<JButton>(); // ERROR! Can't do this.
|
There is a way to tell the compiler to take any generic subtype of the declared argument
type, as long as you promise not to add anything to the collection. That mechanism is the wildcard
<?>. Following is an example:
import java.util.*;
public class testWildcards {
public static void main(String[] args) {
List<Integer> intlist = new ArrayList<Integer>();
List<Float> floatlist = new ArrayList<Float>();
intlist.add(1);
intlist.add(4);
floatlist.add(3.5f);
floatlist.add(2.6f);
viewFirstItem(intlist); // Prints 'The first item is 1'
viewFirstItem(floatlist); // Prints 'The first item is 3.5'
//AddToListBad(intlist, 2); // Can't do this.
AddToList(intlist, 2); // You CAN do this.
}
public static void viewFirstItem(List<? extends Number> numlist) {
System.out.printf("The first item is %s\n", numlist.get(0));
}
//public static void AddToListBad(List<? extends Number> numlist, Number arg) {
// The following line causes a compile error, because you can't
// add to this list with a <? extends Number> parameterized type.
// numlist.add(arg);
//}
public static void AddToList(List<Integer> numlist, Integer arg) {
numlist.add(arg);
System.out.printf("Successfully added %s to list.\n", arg);
}
}
The AddToList method accepts an argument of type List<? extends Number> - that means it can accept a list of
any generic type that extends Number. Therefore, it can accept a list of Integer, or a list of
Float, etc. You can read from items in the list, but you can't add new items to the list.
|
| The List<? extends XXX> format can be used to accept lists of either classes OR interfaces - XXX could be of type Comparable, for instance, which is an interface, not a class. Regardless of whether you're accepting classes or interfaces, use 'extends' (never implements) in generic function signatures. |
| If you were to use List<?> by itself, without "extends XXX", that means a list of any type. You can read items from such a list, but you can't add new items to the list. |
You can't use wildcards when creating new Lists or Collections - they always have to be
a specific type. For instance, the following won't compile:
//WON'T COMPILE
List<? super Animal> lst = new ArrayList<? super Animal>();
But the following will compile:
// Will compile
List<? super Animal> lst = new ArrayList<Animal>();
|
And finally, there is a way to tell the compiler to take a generic list AND still be
able to add items to the list - you must use the '? super XXX'. wildcard syntax.
Examine the following:
import java.math.BigInteger;
import java.util.*;
public class testWildcards {
public static void main(String[] args) {
List<Number>numlist = new ArrayList<Number>();
AddToList(numlist, 2.2f);
AddToList(numlist, 5l);
AddToList(numlist, new BigInteger("2010"));
System.out.println(numlist.toString());
}
public static void AddToList(List<? super Number> numlist, Number arg) {
numlist.add(arg);
System.out.printf("Successfully added %s to list.\n", arg);
}
}
Using Java arrays, an array of Animal can hold an array of Cat or an array of Dog, because
a variable of type Animal can hold an instance Animal or any of its subtypes, including
Dog or Cat.
This is a way to make generics work like that. Saying List<? super Number> says 'I can accept a list of Number, or any of its supertypes.' That means you can't pass in an list of Integer, because that's a subtype of Number - but you CAN pass in a list of Number, or Object. In the same way that a Number can point to an Integer or Float, a List<? super Number> can accept a list of Number, or any objects higher up the hierarchy. To help decide when to use super versus extends in wildcards, use this:
|
| Generic Usage | |
|---|---|
| |
Generic type identifiers can be used in class, interface,
method return type, method argument, and variable declarations:
class MyGenericClass<T>{} class
interface MyGenericClass<T>{} interface
public <T> void getIt() method return type
void doIt(T obj) {} method argument - can also use wildcards <?>
T obj variable - can also use wildcards <?>
| |
| Unbounded types | <T>
|
| Bounded types |
<T extends Number>
|
| Unbounded wildcard type |
<?>
|
| Wildcard type |
<? extends Number> upper bound <? super Number> lower bound
|