1z0-808 Oracle Java SE 8 Programmer – Collections part 2
- Sets
The next type of collection we’re going to look at is the set. And a set is another subtype of the collection interface. Now, the characteristics of a set is that no duplicate objects are allowed and so it’s going to use the equals method to determine whether or not an object is duplicate. Once again, another reason why you want to override equals. Generally speaking, the orders of the elements in a set are not maintained, although there is something called a tree set which will allow you to determine what the order should be as it comes out. But again, generally speaking, the elements of the set are returned in no particular order when you’re using an iterator and sets don’t have or use an index.
The most common set implementations would be the hash set and the tree set. Now, here’s an example of the hash set creating our new hash set like we would any other object. We’re adding strings to the hash set minnesota, Wisconsin, Iowa and Minnesota. Now, since the hash set doesn’t allow duplicate objects, minnesota is already in there. So this second Minnesota will not be added to the set, it will just be silently ignored. And then here’s an example of just seeing if the sales region contains a state that we are getting from the customer. So we’re using the contains method passing in the state.
It’s going to use the equals method to see if that state is in there and it’ll return true or false. Now, the interesting thing here is notice that all the methods we’ve used are pretty much from the collection interface. And not just pretty much from they are, they’re from the collection interface. So that means if I really wanted to, I could change this first line to an array list or linked list and the rest of this stuff would still work. Now the big difference would be that if we did a list, then Minnesota would be there, would appear in the list for the second time with a set. It will not. A tree set is another set that we can use.
It’s known as an ordered set because when we use an iterator and we start to loop through the collection, they will be retrieved in an ordered fashion. So here we’re putting in 88, 53, 60, 612. But when we iterate through them, it’s going to come out in an ascending order. So 1253, 66, 88 and so on. And the reason that this happens is that a tree set is going to rely on a method called compare to, to figure out how it should order these things. There’s a natural order for primitives and strings and the natural order is basically like Unicode it’s numbers first, it’s the capital letters next, and then lowercase letters.
When we’re using a tree set, we can pass in a comparator that will allow us to figure out or rather determine how things should be ordered. So both of these topics of comparator and compared to that’s, something we’re going to cover in the next section. So internally, the tree set is going to use a tree structure to organize the set. But we don’t really have control over that tree. We really only have control of saying how we want the objects handed off to us as we iterate through them. And even though tree sets are ordered, there’s still no index. So we can’t fetch things or remove items from the tree based on a specific index.
- Queues
The next type of collection we’re going to look at is a queue. So a queue is an interface that defines a data structure that has the concept of a head and a tail, which is similar to lines that we stand in. Often we’ll even say that we’re on Q, we’re standing in queue. And what we mean is that we’re in a line where people are being added to the tail and as the next person is able to be handled, they’re being removed from the head. So we have a separate queue interface to add, remove and examine elements in the queue. And we have these different methods because this is such a unique data structure. So within each of the different queue operations, there’s two types. There’s one that can throw an exception.
For example, queues can have a capacity. There’s only so many people or so many things that we can put in the queue. And once we get past that capacity, one or two things could happen. It could throw an exception. Now, we haven’t talked about exceptions yet, that’s going to come up in an upcoming section, but for now, just know that it means that something went wrong. The Java platform lets us know that something went wrong and we can write special code then to handle that. So there is the method add, for example, to add to a queue and if we go beyond the capacity, it’s going to throw an exception. We have a non exception throwing method called offer.
And what’s going to happen is that if we go beyond the queue, we could look at the return type that comes back and if it’s false, then that means whatever we tried to add to the queue wasn’t successfully added. No exception is thrown, but we can check the return value. Other than that, we’ve got removing from the queue. You can call remove, you can examine the head of the queue, you can call element. And again, these are exception throwing methods. The nonexceptions are offer, poll and peak. Of all the queue implementations we have, the most popular would be array blocking queue and priority queue. So let’s look at the array blocking queue. This is one that implements a first in, first out queue.
So the head of the queue is the element that’s been there the longest time. The tail of the queue is the element that’s been in the queue the shortest time. So as new elements are added, they’re added to the tail. And as elements are pulled out or examined, that happens from the head. Now, we’ve been talking about collections having a dynamic size. Array blocking cues hold a fixed number of elements. So when we create the queue, we’re going to create a capacity. Once that capacity has been created, it cannot change. So here’s an example of using an array blocking cue. We’re starting with a capacity of five. So new array blocking Q five and then we’re adding the first, 2nd, 3rd, 4th, 5th.
Notice we’re using the method offer, these will not throw an exception. And then we’re trying to add another one, Hal Jordan, that will just simply not be added to the queue. And if we were to look at the return value, we’d see that we get a false. So now we try to print out the queue and we see all the names except for Hal Jordan. And then when we call Q Poll, what that’s going to do is remove the first one on the head. So we call Q Poll and we print it out and that will be Bruce Banner. That’s going to be the very first element. And then we are calling Q peak. And what peak does is it says tell me what, give me a handle to the reference of what’s ever at the head but don’t remove it from the collection.
And by the way, for the removing we could have instead of just calling Pole, we could have called remove and for the peak we could have also called element. Now that’s an array blocking queue. We also have a priority queue. And that’s kind of like if you’re at an airport, I see this all the time that there’s a couple of lines right there’s the line that the first class people get to walk through the frequent flyers and then there’s the rest of us that have to watch them bypass us. That’s the way that a priority queue works. We can assign different priorities to the things that we’re adding to the queue. And so it’ll sort of order itself based on these priorities.
So anything with a higher orders move to the head of the queue. Now how does it determine that? Well, it’s going to determine that by the natural ordering. And once again, that is a concept that we’re going to talk about when we look at comparator and comparable in the next section. The nice thing about a priority queue is that it’s unbounded in regard to capacity. So we don’t have to specify how many elements will be added to this collection. We can just keep adding as we need to. As elements are added to the priority queue, it’ll just keep growing. So here’s an example of adding five strings to the priority queue.
So we’re creating the priority queue up above here. We’re then adding them through the offer method, clark Kent, Bruce Banner, Hal Jordan and so on. And look what happens as we start to remove these objects and see what we have. They’re coming out in alphabetical order by first name. So Barry Allen, Bruce Banner, Bruce Wayne, Clark Kent and so on. So what happened is that it used that natural order and decided based on strings that it would order everything by number first, then capital letters and then lowercase letters. And of course we can customize that by using these comparator objects, which is something we’ll talk about in the next section.
- Deques
There’s another type of Q that’s called a deck. And a deck allows elements to be inserted and removed at both the tail and the head. So deck is short for double ended Q. And as you’re hearing me pronounce it, it’s normally pronounced deck. Typically there’s no capacity on the number of elements allowed in a deck, so it can grow and shrink as it needs to do. So there’s an additional interface for a deck for adding and removing and examining elements. And of course this is necessary because now we can do both types of activities on both ends. And just like the queues, there’s two implementations.
We’ve got one that can throw an exception if the operation fails and the other will just return a special value, either null or false if there’s any problems. So let’s go through the different operations. We can add to the head of the deck and that’s going to be either add first, which if something goes wrong, it’ll throw an exception, or offer first, which doesn’t throw an exception but can return false. So that’s adding to the head of a deck. We can remove from the head of a deck with remove first or pull first. We can just examine the head, tell us what’s there, but don’t actually remove it from the collection. There’s get first and peak first and then we’ve got the other end. So we can now add to the tail.
So add last and offer last remove from the tail. So remove last and pull last. And finally examine the tail of a deck with get last and peak last. One of the more common implementations of the deck interface is called an array deck. And there’s no size limit to the array deck. It can grow, it can shrink as it needs to. There is a limitation. You can’t put a null element into an array deck. And also array deck objects are not thread safe. That may sound like a bad thing. We talked a little bit about this before when we talked about string builder and string buffer. But in general, if something doesn’t need to worry about being threadsafe, then generally you want an object that is not thread safe because it’s going to perform a lot better.
So here’s an example of an array deck and let’s kind of look and see what’s happening here for the output and how it’s built. So we created the array deck. We’re not specifying a capacity. And the first thing that we’re doing is we’re adding center and so we’re doing offer first, which means put it at the head. Then we’re saying offer first, Bruce Banner. We could have also called add first, but we’re just using the offer first variation. And now let’s look down at the output that we’re eventually going to get to. You can see we’ve got center and then Bruce Banner was added to the head there. Normally with a queue we would have added to the tail. This is kind of cool.
We can add to the head. And now they’re saying, offer last, Barry Allen. So that would be right behind center. That’s right down here. And then offer first Bruce Wayne. Offer last, Clark Kent, and so on. So you can see what they’re doing. They’re just adding to the tail, adding to the head, adding to the tail, and so on. Then what they’re doing is they’re printing out that whole deck. They’re calling Pole last, which in this case is Hal Jordan’s, the very last thing. Then they’re calling Pole first, which would be Peter Parker. And then when they print the deck out again, you see that they remove the things that they had pulled.
- Maps
The final collection that we’re going to look at is a map, and it’s part of the collections framework. It’s in the Java util package, but it is not associated with the collection interface. It’s not a part of that hierarchy. And that’s because map has some unique requirements. It has a unique data structure. So we have a totally different interface to use with that. Here’s the hierarchy for maps. Again, not the complete hierarchy, but some of the more important pieces. We’ve got the map interface at the top and there’s an abstract map class right below that. The two most popular implementations are hash map and tree map. The hash table that’s here, that’s considered legacy.
It’s really not used that much anymore. So a map is a collection of data that’s associated as key value pairs. Specifically, maps have the following characteristics for the keys. They are object keys rather than a numeric index. And that key is important. So we use that key to add objects to the map, to remove objects and also to do sorting. There is a rule that you cannot have any duplicate keys. And if you think about that logically, it makes complete sense. If I duplicate keys and I’m trying to retrieve a specific object, how does it know which object to grab? So no duplicate keys allowed. As far as what makes up a key, it can really be any object, but typically it’s going to be a string or an integer.
And you’ll notice that that’s an integer with a capital i. That’s a wrapper class. We’re going to talk more about wrapper classes in the next section. But an integer with a capital i, that’s a class and it wraps an int. It turns an int into an object. Even though we can’t have duplicate keys, we definitely can have duplicate values just as long as the values are associated with a different key. So let’s look at hash map. That’s probably the most popular implementation of the map interface. We create the hash map just like we would any other object using the new keyword. And then what we’re doing is we’re creating three my date objects. So there’s a birthday, an anniversary, and a tax day. And now we’re going to put those objects into our hash map.
We do that with a method called put. Notice it’s put and not add. When we use the method put, we have two parameters. The first one is going to be the key. In this case we’re using a string for the key called birthday. And then the second parameter is going to be the value, which in this case is the my date birthday object. That’s what we mean when we talk about key value. Pairs do the same thing for anniversary and the same thing for tax day. And then when we want to retrieve one of these values, we say mapget and we pass in the key. Now in the next section we’ll talk about generics and we’ll see a way that we can get out a mydate object without having to cast.
But for right now, note that you are going to have to cast. So we are casting this object to be a My date. Otherwise when it comes out it’ll just be a regular type Java laying object. And the reason for that is polymorphism. When we look at the hashmap put method it’ll take an object and an object. Those are the two types it takes. It takes Java lay object and Java lang object. So what does that mean polymorphically? What could we pass in when a method says it accepts Java laying object? And the answer is all objects.Right? Because when you look at the top of that object inheritance chain at the very top will always be Java laying object.
So that’s a nice way to make the collection very general so that it can accept any object. The problem is then when we pull out the object, the reference is now of type object. So we have to cast it. So that’s what we’re doing here. We’re calling get, we’re accepting the birthday and then casting that to be a My date. We can also remove based on a key. So here’s the string tax day we could remove on that and so on. So as you can see, when you really want to use a hash map is when you’re doing a lot of random access. When we’ve got a lot of objects together, we typically don’t care how they’re organized, just that we want to be able to look something up by an ID.
That’s random access and that’s what hash maps really shine. Here’s kind of an example of what it looks like to be a hash map. It’s similar to a phone book where the key could be the name and the phone number could be the value. Now, this isn’t a perfect example because we know that in a phone book a lot of people can have the same name. But imagine that that wasn’t the case, that every name would be unique. That’s pretty much how a hash map works. So the put method is used to take a specific key and a specific object and then link them as a pair. So that gets known as a key value pair. And when we want to retrieve that object, as we’ve seen you use the Get method and you pass in the key.
As an argument we have the remove method so we can remove an object by the key. And then we also have a method called Clear where we can just remove all of the key value pairs from that map. If you want to check to see if a key or a value is in the map, you can do that with the contains key and contains value methods. And both of these methods will return a boolean telling you whether the key or the value is in the map. Here’s an example of that. So map contains key birthday. I pass in the key and it’ll tell me if that key is in the map or map contains value. I can pass in a specific value object and then it will double check to see if it’s in there.
Using the equals method, by the way, underneath the covers, the way that it’s organizing things into containers is it’s using the object’s hash code. So this is one of the reasons why you want to sort of synchronize your equals method with your hash code method. There’s some other methods like keyset and values. Keyset is going to return a set. We just talked about sets that will have all of the key objects and then there’s values which will return a collection of all the values. So here’s an example of that. We’ve got our map and we say key sets and that returns us a set of all the keys. If I say map values, that’ll give me a collection of all the values.
Now, can you make a guess as to why one returns a set and one returns just a general collection? If you want, put it on pause and think about it for a second. The reason is that keys have to be unique. We can only have one key and we can’t repeat that key ever again in that particular map. So a set is a collection where everything is unique. It doesn’t allow duplicate objects. So it makes sense to get back a set, but the values there could be duplicates so we can’t get back a set. In that case, it’s just returning to us a general collection. So that’s the hash map. There’s also a tree map and the two are comparable to hash set and tree set. So tree map is a way for us to declare some sort of an order to how the items come back out when we start to iterate through the map.
- Collections Lab
In our collections lab, you’re going to be working with Aryliss. And if you decide to do the bonus lab, remember that’s written right at the very end of the lab instructions. You’ll also work with a hash set. Now, you may notice that there’s a lot of warnings on my screen right now. A lot of yellow underlines and little exclamation points on the left side. And the reason is because we have not talked about generics yet. So when you see this in your lab, don’t worry, that’s okay. In the next lab, we’ll actually write some more code that will get rid of all of these warnings. So you’re going to do lab number 15, collections. The PDF is in the resources for this lecture, and let me know if you have any questions.