1z0-808 Oracle Java SE 8 Programmer – More with Collections
- Section Overview
We’re going to add a little bit to our concept and understanding of collections. We’re going to talk about generics and we’re also going to talk about how to sort and search through our collections.
- Limitations of Collections
So I think you’ll probably agree that collections are much, much easier to use than an array if you don’t really know what size you’re going to be working with or if that size is going to grow or shrink over time. And that’s what’s great about collections. They can grow as we need them to and they can shrink as we need them to. But there are some limitations to collections. The first limitation is that you can’t store primitive data types. And that’s that’s sort of a true statement. A collection can’t store a primitive. It can’t, we couldn’t just directly put a primitive into a collection, but Java has been fixed to help us get around that limitation. Basically, collections can only store object references.
So when we see code like this, creating an array list, creating an INTJ equals seven, and then adding the J, you will be surprised or maybe not to find that that actually works, but that’s a primitive. And I just said that you can’t add primitives to a collection. Well, what happens is that we use what’s called a wrapper class. It’s an object whose primary purpose is to wrap a primitive so that it can be treated like an object. In Java sen five and greater, this process happens automatically. It’s referred to as autoboxing. And I’ll show you some examples of that in a little bit. But here are the wrapper classes. And by the way, if you’re going to take the exam, you want to study the API on each of these wrapper types so that you’re very familiar with what it does and all the methods that are available.
But basically every primitive type is going to have a corresponding wrapper type. So byte with a lowercase B is the primitive and then byte with a capital B is the wrapper type. And most of the object wrapper types are the same name as the primitive, except for like int versus integer. So it’s the full name, char versus character. Everything else though, is exactly the same name, but it has a capital letter. And this kind of shows you how conventions in the way that we code can really help readability and understandability. I know when I see a type with a capital letter that it’s not a primitive, that it is a class. So conventions can help our understandability of code. So wrappers have a couple of uses.
We can use them to add primitives to collections. We can attach functionality to primitive data types. Remember, one of the rules about primitives, not rules, but to facts, I guess, is that a primitive has no behavior. There are absolutely no methods that we can call on a primitive. But when we use these wrappers now, we can add behavior, we can add functionality to these data types. So to create a wrapper instance, we just pass the primitive either into the constructor for the wrapper, or better yet, use this method called value of value. Of just happens to perform a little bit better than using the constructor. But you can use the constructor if you want.
You could say new integer and pass in an int but I’m just going to encourage you to use the value of method instead. So here we’re creating an array list. We’re setting J to be seven and then what we could do is say integer call this static method value of passing in the int that will create a wrapper of the object and then we can add it to the collection. If we want to get the primitive back out of the wrapper object then we just call the value method. So each of the different wrapper classes have different named value methods. It’s whatever the type is and it’s concatenated with the value. So in other words, we have an int that we’ve wrapped.
So wrap intvalue. If I want to get the character value out of it, I have char value and so on. And so that will pull the wrapped value out of the wrapper. So that’s the first limitation of collections and honestly, it’s not really that much of a limitation, at least from a usage standpoint. I showed you a code example that made it look like we were storing primitives into the collection and that’s really how it works on a day to day basis. That’s called autoboxing. We’ll see it in action in just a little bit. But the second limitation is that we can only add object references and in a way that’s a feature because with polymorphism that means that we can add any object to a collection.
In fact, we could have a collection with lots of different kinds of objects if we needed that. So in a way that’s a feature. However, what happens then is that the collection gets generalized. And so as we try to retrieve objects from the collection they come out as general objects which means we have to cast. Let’s look at another example where we can see that again. So here I’m creating my array list and am creating an employee, setting the name of that employee and adding it to my array list, doing the same with the secretary and so on. And now when I try to get the element number one I have to cast it. I have to cast it back to being an employee. Otherwise it’ll just be an object.
- Generics and Autoboxing
So to help deal with these limitations, two things were added back in Java Five called generics and autoboxing. So let’s look at some examples of how these issues were dealt with. So here’s an example of trying to store ints into an array list. What we would have had to do is wrap each individual array list into an integer wrapper class. Explicitly we would have had to say new ints integer or in this case, say integer value of. Now in this case, everything that we’re putting in the array list is of the same type. It’s homogeneous. But in order to get the int out of the list, first we’re going to have to cast the object into an integer and then extract it from the wrapper object.
And it would look something like this. This is just to get a single value out of the collection. So first we’d say primes, let’s get the value at index number one. So we’re really trying to get this one right here the number two. That’s a little confusing because we’re using integers. But when you say primes get, when we say ArrayList get, what we’re specifying here is not the number we’re trying to match, it’s the index. So get not the first one because that would be index zero, get the second one, which is index number one. And what do we have at index number one? That would be the number two. Hopefully you follow that. So we get that value and we cast it to the integer type.
Then once we have the integer type to pull the int out, we would say intvalue and that would give us our intx. And if you’re looking at that and thinking that’s pretty crazy, well then you know how all of us felt back when Java one four was available and earlier. So they gave us a way to specify what’s going to be inside of the collection. And it’s called a generic. Generics are not limited to collections, but it’s one of the most common places you’ll see them. And so what a generic says is that I hold anything, I’m generic. But you can specify a parameter type and you do that in between the brackets that you’re seeing here.
So what we’re saying is we’re going to have a reference variable that’s type list, but specifically the parameter type is integer. And then we’re creating our new array list and right next to the type, once again we’re specifying that this array list that we’re creating is going to hold integers. So now we add all of those integers to the collection. And when we get it out, notice that I didn’t have to cast it. I can just say primes get one and then call intvalue because it knows that it’s going to be an integer, so it returns an integer type. Now I still had to call intvalue to get the wrapped int, but we’re a little closer to reducing the code we write.
So if you go to the Java docs, you’ll see this syntax in a lot of different collections and even different classes. What you’ll do is you’ll see for example, bracket e bracket. And then when you look to see what that type parameter is, it’ll say E is the type of elements in this collection. And then as you dive down deeper into the methods you’ll start to see that whatever you specify as E can then be used in methods. So here’s an add method and it takes whatever element we specified we would use. Again, going back here, that’s what we did. We said we’ve got an array list and what we’re going to put in there is integer. So the add method is expecting something that is of type integer.
Again, that’s known as a parameter type. So that solves the first problem of having to constantly cast our object references. The second was that the wrapper first of all the wrapper doesn’t go away. But what Autoboxing does is it automatically wraps or unwraps the primitive. So let’s look at some code. Here’s how it works. In many cases when I have an int, if I am going to be storing it in a wrapper type, then Autoboxing will automatically wrap it for us. So prior to Java five this would not have been legal. We would have had to say new integer or we would have had to say integer value of. But now we can just say here’s the int and automatically wrap it into this wrapper type.
So this does compile, it prints out five at runtime and the underlying bytecode that is generated is exactly the same. It’s as if we had wrapped it ourselves. So Autoboxing is really just what we would call syntactical sugar coating. And we can go the other direction too with unboxing. So now we’ve got our integer, our wrapper object here and we’re going to store it in an int. Well, then it will call the int value method for us automatically. So when we print out Z, that’ll print out the int five and ultimately auto boxing and generics working together just really helps us keep our code cleaner.
So first of all, notice with the generics that we are still using the wrapper type. Again, Autoboxing doesn’t mean that the wrappers go away and that we can actually add primitives, it’s just that it’ll do the wrapping and unwrapping for us. So in this case here we’ve got primes add 1235 and seven and it really is UN underneath the covers. It’s generating the code that is wrapping it in a wrapper type. So it’s doing the boxing, the auto boxing. And then when we want to get a value out, all I have to do is say primes get one. I don’t have to cast it to anything, I don’t have to unbox it, unwrap it, it just arrives as it should.
- Diamond Operator
So generics allow us to be more type safe when we’re coding. We can say specifically what it is we’re trying to put into the collection and therefore give us what we’re expecting when we pull out those objects. However, adding the types to both the reference and the instances can be a little tiresome. Like for example, look at this code here we’ve got our array list and on the left side we’re saying that the type is string and we’re also saying it on the right side. And I’ll tell you, developers just hate typing. We just don’t want to do this. We just don’t want to say that it’s type string on both the reference variable and when we’re instantiating the object we feel that it’s redundant.
So in Java Seven, a new operator was introduced. It’s called the diamond operator and it’s really there to just reduce typing. It’s called the diamond operator because it looks like a diamond. It’s the less than and the greater than symbol put together. So when we use the diamond operator only the reference has to be specified with generics. And when we instantiate the object it’s going to use that diamond operator to suggest that it’s going to be the same type as whatever the reference variable is. So our array list would look like this. Now we’ve got the array list which is of type string and over on the right side, where we’re actually instantiating the object, we use the diamond operator and it’s just repeating whatever is here.
The diamond operator gets to be a little bit more interesting when we have data structures within data structures. Let me show you a code to explain this. So here I’ve got a list and the list, each element of the list is going to be a map. And a map is built of an integer key and a string object. So that’s a little more typing involved. If we didn’t have the diamond operator, we’d have to repeat that on the right side. But now with the diamond operator we only have to list the list, map, integer and so on on the left side and on the right side we just simply use the diamond operator.
- Sorting and Searching Collections
When we were working with the Arrays type, we saw that there was an Arrays class and it just had a whole bunch of nice methods for sorting, searching and filling and so on. Collections has the same thing. It’s a method or excuse me, it’s a class called Collections and it does all the same kind of things search, fill, shuffle and so on. If you’re taking the exam just like the Arrays class, this is something you’re going to want to study. Go to the API and look at all the different methods that are there, but let’s look at some of the more popular ones. Now there’s a sorts method that we can use to sort any kind of a list. So in this case here, we’ve got our array list. We’re adding a bunch of names and then we’re calling Collections Sort.
We also have the binary search method, which will then try to find an element and tell us what index it’s at. Binary searches, as we mentioned before, are pretty efficient, but they rely on the collection being sorted. So in this case here, we could do a Collections binary search on our list. In this case. Looking for Wally West. We can also change the ordering of a list. So Collections reverse Collections Shuffle and that will change the list based on what we’ve called. So there’s a bunch of different methods that are available in both the arrays and the Collections class. Even if you’re not taking the exam, it’s a good idea to load up those APIs in the Java doc and take a look and see what they can do for you.
- Comparable
In the previous lecture we saw that we could sort a list with that collections class. And in a previous section we saw we could do the same kind of thing with an array. We had an example of a Char array doing the same thing in both cases, collections and arrays. We’ve seen that these can be automatically sorted without any help from us saying how it should be sorted. And the way that it does that is that with strings and primitives they all have something called a natural ordering. And the natural ordering is going to be following Unicode. So the highest precedence in ordering would be numbers, then capital letters and then lowercase letters.
But if we’re going to sort custom classes, then we’re going to have to implement the comparable interface to help the sorting methods understand what they should do. And this is the same case for both the collections class and the arrays class, which provides the sorting method. So here’s an example. We’ve been working with a My Date object. It’s got an int day, an in month and an in year. And let’s say that we’re going to create an array of these My dates. So we make an array of my dates and then we say let’s sort them arrays sort. And obviously if we had done a collection like a list, then we would have had collections sort.
Either way we would have seen the same error, which is what we’re seeing below, right down here. And it’s saying we’ve got a class cast exception, my Date cannot be cast to Java line comparable. And by the way, this is a runtime exception. We wouldn’t see this when we compile it’s when we would run. But what does the exception mean? Well, what it’s trying to do is it’s trying to cast it to this comparable interface. Because if our class implements a comparable interface, then it knows that it can call a method called compare to. So if we want our custom objects to be sorted, then we have to implement this interface and this is what it looks like.
We would say that we implement comparable and then we have one method to implement and that’s compared to. Now compare to takes a generic object. So what we’re going to first have to do is cast the object that we’re being compared to to a My date and then we’re just kind of going through and we’re just trying to see, look, if the year is not the same then let’s subtract my year from the year that was just passed in. And what’s going to happen is that means it’s either a return, a positive number, a zero or a negative number. If I return a positive number, then that means this date, the My Date object that I am right now, should come after the year that you just passed in.
If I return a negative number, then that means I come before the year that you just passed in and if I return to zero, then that means we are the same year or we’re the same, we are equal. And so we’re just kind of going through and checking the year, the month and the day and we’re just trying to see whether or not we should come before or after or if we’re the same. And that may feel like we’re doing a lot of the work, but in fact it’s really the sorting algorithm that’s the most difficult part to implement. So we’re just simply saying you’re comparing me to someone else. I’ll tell you if I come before if I’m the same or if I’m after.
And once again I return any kind of a positive number if I should be sorted after the object you’re comparing me to, or a negative number if I should be sorted before the object you’re comparing me to, and zero if we’re equivalent. So with that example, all the my dates are going to be sorted in ascending order. If we wanted to swap that, if we want to go in descending order, we just need to change our math. So now I just kind of flip the numbers around. Now I’m returning the other objects year minus my year. So let’s imagine that the other object is 2000 and I’m 1950. So now if I want to go in the opposite direction, if their year is more, which it is 2000 and I’m 1950, I’m returning a positive number. So I’m saying should be sorted after that year. In other words, we’re going to go from 2000 to 1950.
- Comparator
The reason that we often will implement the comparable interface is because there are a number of classes that will try to cast us to be a comparable because they’re doing some kind of sorting that could be in the arrays sort method or the collection sort method. We’ve seen that there are collections such as tree set and tree map that will use a comparable to find out how you should be sorted. But there’s another option that we have as well and it’s called the comparator interface and it’s a little bit more flexible. The comparable interface is saying we are going to be compared, this particular object is going to be compared to another whereas the competitor is kind of this third party. It says give me any two objects and I will tell you how they’re ordered.
And what we can do is we can pass in this comparator object as another parameter to the sort methods in arrays, collections and other classes. So the sorting object is just one that implements the comparator interface. This interface is located in the Java Util package just like comparable and any class that’s going to implement this interface has one method to implement. It’s called compare. It’s very similar to compare to, but Compare will take two objects as arguments, the objects that are going to be compared and then what’s going to happen is the compare method will return a positive number if the first argument is larger than the second, meaning that the first argument should come after the second.
Zero if they’re the same and negative if the first argument should come before the second argument. So here’s an example of a comparator. We’ve got our compare method. Everything that’s coming in is just a generic type object so we’ll have to cast it. And then in this case what they’re doing is they’re starting to compare the dates and so they start with the year and they say if it’s not the same year, let’s figure out which year comes first. And so they subtract D one year from D two year. Let’s imagine that the first one is 2000 and the second one is 1950. So that would return a number of 50, that’s a positive number.
So they’re saying that the first year should come after the second. And if the years happen to be the same then they would check the months and if the months are the same then they would check the days. Otherwise it’s returning zero, which means they are equal. If we wanted to go the opposite direction we just flip our numbers just like we did in the compare to method. So now we’re subtracting the D two year from the D one year. Just we flip those numbers around and if the numbers were still the same, if D one was 2000 and D two is 1950, now what we’re returning for that year is a negative 50.
So we’re saying that the first argument should actually come before the second argument. In the previous examples it was assumed that both of the objects are my dates. But it doesn’t have to be that’s. One of the powerful aspects of a comparative is that it can compare any two objects. Now, when you’re comparing like objects typically the comparable interface will be used. The compare to method and the power of the comparator is that you’re able to compare two objects that aren’t even related. So if you have some kind of a collection that has a lot of mixed stuff, you’re definitely going to want to use a comparator instead of a comparable.
- More With Collections Lab
In this lab. We’re going to fix all of the warnings that we just had in the previous lab. We’re going to add parameter types to our collections, and in addition, you’re going to make good a comparable object so that we can sort through our catalogs. The instructions for this lab are in lab 16 more with collections PDF that’s going to be found in the resources. If you run into any trouble, the solution code is at the end and let me know if you have any questions.