views:

1107

answers:

7

I'm trying to understand the API inconsistency in the Java standard collections library.

There is no method in List or in AbstractList to get the last item, although one can simulate that with size and getIndex().

However, LinkedList supports that function.

Any idea why it was decided not to support this method in the interface?

+2  A: 

An interface's goal is to provide the maximum usability with the lowest possible number of public methods. The less methods there are to support, the better. There is no getLast() method because this can be derived, as you said.

LinkedList, on the other hand is a concrete implementation, and therefore doesn't have these issues.

Edit: as Skaffman pointed out, this is not the main goal of an interface. It is more a secondary goal which allows implementations of this interface to be easier to maintain. The main purpose of an interface is to decouple the concrete implementation from the objects that use it.

Zack
No, an interface's goal is to seperate implementation from interface. The type of operations have nothing to do with it.
skaffman
I'm not sure I agree, but I guess it depends on how likely someone is to want the last item from a list.
Uri
I wonder what other list implementations are possible apart from the built in LinkedList/ArrayList in witch a common getFirst() getLast() interface method wouldn't have been O(1)?
kd304
@skaffman: You're correct. That is the *main* goal of an interface, of course. I'm talking more from the perspective of a secondary goal. I will update my answer to reflect this.
Zack
+5  A: 

Usually the reason is that they wanted to specify each function with a Big-O requirement and felt that getLast() could not be implemented efficiently on all lists. So they introduce it at each level with its Big-O promise.

Or it could just have been an oversight, or they felt it wasn't common enough and if you needed it you could get it with size/getIndex.

Lou Franco
I sometimes wish they had added it to ArrayList too at least. Writing arraylist.get(arraylist.size() - 1) over and over again is kind of a boilerplate.
kd304
public class MyArrayList<E> extends ArrayList<E> { public E getLast() {return get(size()-1);} }
Charlie
@Charlie: Unless you work with a list interface and don't want to ever directly know what's your underlying implementation.
Uri
I guess Charlie would solve this issue by having a MyList extend List and MyArrayList implement MyList.
kd304
+1  A: 

As you said, with List you can get the last one with getIndex(), since it's index based. So personally I do not see any good reason to put it (since you can write it yourself).

While a LinkedList is not array based, so no indexes, it makes sense to provide a method like that, sometimes you need to know the last item.

Alexandru Luchian
A: 

getLast() couldn't be implemented in a one-way linked list. getIndex() on a one-way linked list would probably scan the entire list from the beginning so it has an efficiency implication.

Unless it just stored the last element in a field. How do you think getSize() is implemented?
Michael Myers
This is why java's LinkedList is double linked or in single linked case the list just holds a reference to its last element to allow O(1) addition at the end of the list.
kd304
+5  A: 

The java.util.List interface doesn't support getLast() because the designers went for a 'minimal interface'. With the minimal number of methods defined it makes it both easier to understand and quicker to learn.

This is in contrast with a 'humane interface' (such as used in the Ruby array class) which attempts to provide methods for doing common operations (e.g. getLast()). As there are many uses which such a fundamental concept as a list can be put to this tends to lead to much larger interfaces.

For further information see Martin Fowler's Minimal Interface and Humane Interface descriptions.

As to why LinkedList supports getLast() etc., to quote the javadoc:

... the LinkedList class provides uniformly named methods to get, remove and insert an element at the beginning and end of the list. These operations allow linked lists to be used as a stack, queue, or double-ended queue (deque).

Presumably it was felt that a general List would not be adequate for these specific use cases.

As an insight into the mind of the principal designer of the Java Collections API (Joshua Bloch) he provides this list of API design maxims by which he works. Of which, the most pertinent to this question are:

Early drafts of APIs should be short, typically one page with class and method signatures and one-line descriptions. This makes it easy to restructure the API when you don't get it right the first time.

When in doubt, leave it out. If there is a fundamental theorem of API design, this is it. It applies equally to functionality, classes, methods, and parameters. Every facet of an API should be as small as possible, but no smaller. You can always add things later, but you can't take them away. Minimizing conceptual weight is more important than class- or method-count.

Keep APIs free of implementations details. They confuse users and inhibit the flexibility to evolve. It isn't always obvious what's an implementation detail: Be wary of overspecification.

Minimize accessibility; when in doubt, make it private. This simplifies APIs and reduces coupling.

Consider the performance consequences of API design decisions, but don't warp an API to achieve performance gains. Luckily, good APIs typically lend themselves to fast implementations.

However he also states:

Don't make the client do anything the library could do. Violating this rule leads to boilerplate code in the client, which is annoying and error-prone.

Which just shows that design guidelines often conflict and the hardest part of an API designers job is to balance these conflicts.

Matthew Murdoch
+1  A: 

The getLast() method is from the Deque interface that LinkedList implements. If you wanted an array backed version of that, you could use ArrayDeque . I guess it's not part of the List interface because they wanted to seperate out different abstract data types into seperate interfaces, even though an implementation like LinkedList can implement more than one interface.

Daire
The Deque interface was only introduced in Java 6. The LinkedList class (with its getLast() method) predates this by several versions (introduced in 1.2).
Matthew Murdoch
A: 

The List interface doesn't have any convenience methods of that type. No getFirst() or getLast(). It is strictly index based. LinkedList needs a way of avoiding index based lookups if possible for performance reasons, so getLast is really an optimization, not a convenience method.

I'm sure the designers wanted to keep the amount of methods down, so they didn't add a lot of potentially convenient methods.

Yishai
If you look at the LinkedList implementation it contains an optimization for the index based lockup: If the index is after the half size of the list then then the traversal starts at the end and goes backwards. This way a get(size - 1) is practically has the same time cost as getLast().
kd304
@kd304, that is an implementation detail that doesn't address the general case of index based lookups not being optimal for LinkedList. There is no contract on LinkedList that states that certain index based lookups will be fast.
Yishai