Monday, November 22, 2010

google's guava library tutorial part 5: back to the primitive(s)

In the fifth part of my tutorial, I'll try to show you the Primitives package of Google Guava library. Primitives package offers several classes to work on primitives or arrays of primitives which are not found in the corresponding wrapper classes (e.g. Boolean for booleans) or Arrays. I will begin with Ints which's the helper class for int related operations. Although it's open to discussion, int is probably the most used primitive type and most of the helper methods are shared among classes in the Primitives package so when I explain Ints, I cover the majority of the methods in the Primitives package.

There are two cast methods in Ints for casting long to int. The first one, saturatedCast(), returns the nearest int value to the given long.



// max value of long is too large for int
// thus it'll cast to the nearest int
// which's the max int value

int saturatedCast = Ints.saturatedCast(Long.MAX_VALUE);
System.out.println(saturatedCast);
//2147483647
System.out.println(saturatedCast == Integer.MAX_VALUE); // true




// min value of long is too small for int
// thus it'll cast to the nearest int
// which's the min int value

saturatedCast = Ints.saturatedCast(Long.MIN_VALUE);
System.out.println(saturatedCast);
//-2147483648
System.out.println(saturatedCast == Integer.MIN_VALUE); // true



// 0 is available in int range [-2147483648, 2147483647]
// so the nearest value to cast
// is itself in int

saturatedCast = Ints.saturatedCast(0L);
System.out.println(saturatedCast);
System.out.println(saturatedCast == 0); // true


You'll probably use saturatedCast in cases where using the approximated value wont cause you any problems. In cases you can't take that risk you should use checkedCast(). In cases where you try to cast a long out of int range this method will throw you IllegalArgumentException.


long long1 = Integer.MAX_VALUE;
long1++;
int checkedCast = Ints.checkedCast(long1);
System.out.println(checkedCast);


You will get java.lang.IllegalArgumentException: Out of range: 2147483648 (which's max int value + 1)

min() (max()) returns smallest (largest) integer in the integer array. Collections class has similar min/max methods but it was not straightforward to transform an int array to a Collection.



int[] integerArray = new int[]{3, 5, 7, -3};

System.out.println("Max: "+ Ints.max(integerArray) ); // 7
System.out.println("Min: "+ Ints.min(integerArray) ); // -3


// There are two indexOf methods
// if we dont count lastIndexOf()

// The first one finds the index of the given integer
// in the given array
int indexOf = Ints.indexOf(integerArray, 7);
System.out.println("index of "+7+" in "+Ints.asList(integerArray)+" is "+indexOf);
// index of 7 in [3, 5, 7, -3] is 2

// The second one finds the beginning index of the second
// given array
// in the first given array
indexOf = Ints.indexOf(integerArray, new int[]{7, -3});
System.out.println("index of the given array in "+Ints.join(",", integerArray)+" is "+indexOf);
// index of the given array in 3,5,7,-3 is 2

// If it couldnt find the given value
// it returns -1
indexOf = Ints.indexOf(integerArray, 6);
System.out.println("index of "+6+" in "+Ints.join(",", integerArray)+" is "+indexOf);
// index of 6 in [3, 5, 7, -3] is -1



If you have a sorted int array in hand you better use Arrays.binarySearch() if not then use Ints.indexOf().

You can see the use of join() method. You just give a String separator and an int array for obtaining a string presentation of each element separated by the given separator.


System.out.println(Ints.join("|", new int[]{3, 4, 5, 6, 2, 7}));
//output: 3|4|5|6|2|7


There's no good alternative to this without Guava as far as I know. Its not straightforward to convert the int[] to a List so you can call toString() for obtaining a string presentation of int array. You can't use Joiner class either because it's for working on objects rather than primitives.


There's also a lastIndex() method which returns the index of the last occurrence of the given integer



int[] newIntArray = new int[]{5, 23, 6, 24, 69, 6};
int lastIndexOf = Ints.lastIndexOf(newIntArray, 6);
System.out.println("Last index of: "+lastIndexOf);
// Last index of: 5

System.out.println(lastIndexOf == (newIntArray.length-1));
// 6 is the last element so this returns true



indexOf() starts from the beginning and check each element while lastIndexOf() starts from the end. If you just need if the element in hand is part of the given array or not (instead of its place in the array) then you can use contains().


System.out.println("Is there 69 in the array? "+Ints.contains(newIntArray, 69));
//true
System.out.println("Is there 66 in the array? "+Ints.contains(newIntArray, 66));
//false



concat() concats the given int arrays. Ints.concat({a, b}, {c,d}) will return {a, b, c, d}. One of the old ways of doing this was creating an int array with the sum size of the given arrays and copy their content in the new array by either iterating over them or by using System.arraycopy().


int[] concatResult = Ints.concat(new int[]{1, 2, 3, 4, 5, 6, 7},
new int[]{8, 9, 10}, new int[]{11, 12, 13, 14});
System.out.println(Ints.join("|", concatResult));

// 1|2|3|4|5|6|7|8|9|10|11|12|13|14


There's a compare() method for comparing two integers. compare(x, y) returns a negative value if x is less than y, a positive value if x is greater than y or zero if they are equal which means that it works the way Integer's compareTo() works.


System.out.println(Ints.compare(2, 4)); // -1
System.out.println(Ints.compare(5, 3)); // 1
System.out.println(Ints.compare(3, 3)); // 0


Assume that I have a Collection of Integers like below and I want an int array from this collection.


List integerList =
Lists.newArrayList(new Integer(0), new Integer(2), new Integer(-8));

// integerList.toArray() will give me an array of Integers.
// What if I'd prefer an array of int and not Integer?

int[] array = Ints.toArray(integerList);
System.out.println(Ints.join(", ", array));
// 0, 2, -8


ensureCapacity() returns the given array directly if arrays size is more than the given min length. Else it returns a new array of length (min length+padding) and copies the given array to the newly create array. Extra elements in the new array are given default initialization values. e.g. 0 for int, false for boolean.


int[] myIntegerArray = new int[]{1, 4, 5, 7, 3, 2};
int[] ensureCapacity =
Ints.ensureCapacity(myIntegerArray, 4, 3);
// len=4, padding=3
System.out.println(Ints.join(", ", ensureCapacity));
// the same array is returned
System.out.println(myIntegerArray == ensureCapacity);
// true

int[] ensureCapacity2 =
Ints.ensureCapacity(myIntegerArray, 10, 3);
// len=10, padding=3
System.out.println(Ints.join(", ", ensureCapacity2));
// a new array is returned with 0s at the end
System.out.println(ensureCapacity2.length == (10+3));
// true because the size is len+padding



Ints can hand you a Comparator for comparing int arrays. First I create a List of int arrays. I'll sort them so you can see how the lexicographic comparison works


ArrayList < int[] > newArrayList =
Lists.newArrayList( new int[]{1}, new int[]{2},
new int[]{1, 5, 0}, new int[]{2, 5, 0},
new int[]{2, 1}, new int[]{}, new int[]{2, 5, 1},
new int[]{2, 5});
// Sort the collection in hand
Collections.sort(newArrayList, Ints.lexicographicalComparator());
// I do a transformation to see the String presentation
List < String > transform5 = Lists.transform(newArrayList, new Function < int[], String >(){
@Override
public String apply(int[] array) {
return Ints.join("-", array);
}});
System.out.println("lexicographicalComparator: "+ transform5);
// lexicographicalComparator: [, 1, 1-5-0, 2, 2-1, 2-5, 2-5-0, 2-5-1]



Empty array is the first as you can see. When one element is the prefix of the other the shorter one is first (as in the case of 1 and 1-5-0).

You can obtain a live view of the array in hand as a List using asList(). Operations that'll cause a problem in the array wont be supported by the List in hand. Updates are bi-directional which means that if you change the live List view the array changes too, and vice versa.


int[] integerArray2 = new int[]{1, 4, 6, 7};
List < Integer > newIntegerList = Ints.asList(integerArray2);


You can't add or remove an element to the backing list. This operation will throw you java.lang.UnsupportedOperationException. This restriction is probably due to the fact that the underlying data structure is an array which is in fact fixed-sized thus you cant add or remove elements to/from it.


// newIntegerList.add(66);
// newIntegerList.remove(0);
// newIntegerList.clear();


Set operation will work indeed. See the change in the underlying array.


// {1, 4, 6, 7}
newIntegerList.set(0, 2); // put 2 in the zeroth place
System.out.println("set(): "
+Ints.join(", ", integerArray2));
//set(): 2, 4, 6, 7
System.out.println("set(): "
+newIntegerList); // set(): [2, 4, 6, 7]



// Now lets change something from the array.
// See the change in the list
integerArray2[0] = 99;
System.out.println("set [0]: "
+Ints.join(", ", integerArray2)); //set [0]: 99, 4, 6, 7
System.out.println("set [0]: "
+newIntegerList); //set [0]: [99, 4, 6, 7]



There are few byte-related method that I chose to skip. If you're interested in them please check the javadoc. This is all for Ints class.

The methods I showed were identical for most of this package's classes (Booleans, Chars, Doubles, Floats, Longs, Shorts, Bytes). Now I'll try to explain the methods that are interesting and not covered.

When working with bytes you can be indifferent to their sign (Bytes) or choose to use signed bytes (SignedBytes) or unsigned bytes (UnsignedBytes). All these classes contain methods which are already covered. Only UnsignedBytes is worth mentioning.

Each negative byte is mapped to a positive one in this class. A negative byte b is treated as 256 + b. toInt() is the only method that's not previously covered.


int int1 = UnsignedBytes.toInt((byte)-125);
// 256 - 125 = 131

System.out.println(int1 == 131); // true



Primitives class is a helper class for working on primitive or wrapper classes. allPrimitiveTypes() returns the Set of primitive types' classes. allWrapperTypes() returns the Set of wrapper types. isWrapperType() checks if the given class is a wrapper type. unwrap() returns the given wrapper class' primitive while wrap() does the inverse.


Set < Class > > allPrimitiveTypes =
Primitives.allPrimitiveTypes();
System.out.println(
"All primitive types: "+allPrimitiveTypes);
// All primitive types:
// [byte, boolean, void, int, long, short, double, char, float]


Set < Class > > allWrapperTypes = Primitives.allWrapperTypes();
System.out.println("All wrapper types: "+allWrapperTypes);
// All wrapper types: [class java.lang.Byte, class java.lang.Short,
// class java.lang.Float, class java.lang.Character, class java.lang.Long,
// class java.lang.Integer, class java.lang.Boolean, class java.lang.Void, class java.lang.Double]


System.out.println(Primitives.isWrapperType(int.class));
// false because int is not a wrapper type
System.out.println(Primitives.isWrapperType(Integer.class));
// true because Integer is a wrapper type
System.out.println(Primitives.isWrapperType(Object.class));
// false because Object is not a wrapper type

// As I have a set of wrapper types
// I can check if a class is
// a wrapper type or a primitive type.

System.out.println("is wrapper? "+Primitives.allWrapperTypes().contains(Integer.class));
// true
System.out.println("is primitive? "+Primitives.allPrimitiveTypes().contains(int.class));
// true

//both below are true because if I unwrap an Integer
// I obtain an int and if I wrap an int I obtain
// an Integer
System.out.println(
Primitives.unwrap(Integer.class).equals(int.class));
System.out.println(
Primitives.wrap(int.class).equals(Integer.class));



This is all for the Primitives package. Although I think that some of the methods are redundant there are quite useful ones that'll make your life much easier.

6 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Wow Jazzie!! Python or PHP are not going to replace Java..not even in near future
    Abt the tutorial: its really informative, thanks

    ReplyDelete
  3. thx from france for tutorial.
    very clear.
    have a nice day.
    roky

    ReplyDelete
  4. Really enjoy the tutorial. Very nice to use guava to get clear logic of java code. thx.

    ReplyDelete
  5. Thanks! I have aready read all the five tutorials. You are so nice to do this.

    ReplyDelete