3
\$\begingroup\$

I'm a Java beginner, going through the Generics Questions and Exercises in Oracle's The Java Tutorials.

Here's my solution to #8:

Write a generic method to find the maximal element in the range [begin, end] of a list.

All suggestions for improvement are welcome. I'd especially like feedback on the following:

  • Java library usage
  • Algorithm performance
  • Exceptions

(imports omitted for brevity)

@SuppressWarnings("serial")
class MaximalElementList<E extends Comparable<E>> extends ArrayList<E> {
 /**
 * @param begin - list index to start iterating from (inclusive)
 * @param end - list index to iterate up to (exclusive)
 * @return max elem in list as determined by the compareTo of the element type.
 */
 public E getMaxInRange(int begin, int end) {
 E max;
 if (isEmpty())
 throw new IllegalStateException("Can't get a max element from an empty list.");
 else {
 max = this.get(begin);
 for (E elem : this.subList(begin, end)) {
 if (elem.compareTo(max) > 0) {
 max = elem;
 }
 }
 }
 return max;
 }
}
rolfl
98.1k17 gold badges219 silver badges419 bronze badges
asked Apr 8, 2015 at 1:37
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

So, here's the thing, what you have is not a generic method. What you have is a generic class.

Now, it's a clearly defined generic class, but, it is not what the question asked for. (as a generic class it has a number of issues too, but let's get the method/class issue resolved first).

Generic methods

A generic method is just that, a method, except the parameters (or return value) are of a generic type. A generic method always has a <...> structure before the return-type declaration. A normal method is:

public RType methodName(P1Type p1, P2Type p2, ....) {...}

(where RType is the return type, and P1Type is parameter1 type, etc.).

A generic method has the <...> before the return type, and that construct is used somewhere in the method signature... for example:

public <T> RType methodName(T t, SomeType sparm) {....}

The above is a generic method that has the generic type T as a parameter.

Your Class

So, having stated that your solution is a generic class, not a generic method, let's assume the exercise goal was to produce a generic class. What then?

  1. Unless you have exceptional reasons, don't extend ArrayList, "compose" it instead. Have a class that does not extend ArrayList, and have a class field instead, like:

    class MaximalElementList<E extends Comparable<E>> {
     List<E> data = ......
    
  2. Your generics here are OK, no problem with <E extends Comparable<E>>

  3. Your code should use a guard-condition instead of an else... let me explain. Your code has an if/else:

    public E getMaxInRange(int begin, int end) {
     E max;
     if (isEmpty())
     throw new IllegalStateException("Can't get a max element from an empty list.");
     else {
     max = this.get(begin);
     for (E elem : this.subList(begin, end)) {
     if (elem.compareTo(max) > 0) {
     max = elem;
     }
     }
     }
     return max;
    }
    

    That should instead be:

    public E getMaxInRange(int begin, int end) {
     if (isEmpty()) {
     throw new IllegalStateException("Can't get a max element from an empty list.");
     }
     E max;
     max = this.get(begin);
     for (E elem : this.subList(begin, end)) {
     if (elem.compareTo(max) > 0) {
     max = elem;
     }
     }
     return max;
    }
    
  4. Note now, that the max is a messy variable, it can just be:

     E max = this.get(begin);
     for (E elem : this.subList(begin, end)) {
     if (elem.compareTo(max) > 0) {
     max = elem;
     }
     }
     return max;
    
  5. Your use of a sublist is smart, but I would consider it to be overkill in this case. How about a simpler implementation:

     E max = this.get(begin);
     for (int i = begin + 1; i < end; i++) {
     if (get(i).compareTo(max) > 0) {
     max = get(i);
     }
     }
     return max;
    

That is now some logic which I think would work well.

Making it a method

Putting that logic in a generic method would "simply" mean:

public <E extends Comparable<E>> E max(List<E> data, int begin, int end) {
 E max = data.get(begin);
 for (int i = begin + 1; i < end; i++) {
 if (data.get(i).compareTo(max) > 0) {
 max = data.get(i);
 }
 }
 return max;
}

With that method, for example, you could do:

List<String> data = Files.readAllLines(Paths.get("some file.txt"));
String maxLine = max(data, 0, 10);
answered Apr 8, 2015 at 2:18
\$\endgroup\$
6
  • \$\begingroup\$ thanks, your answer has been very helpful on the guard condition and cleaning up of the max variable declaration. Could you help further by explaining the reason why I should compose ArrayList instead of extending it? \$\endgroup\$ Commented Apr 8, 2015 at 5:09
  • \$\begingroup\$ Also, would you not consider the index variable i in your for loop suggestion to be clutter and the double get(i) to be inefficient? \$\endgroup\$ Commented Apr 8, 2015 at 5:11
  • \$\begingroup\$ @doughgle - See the design pattern experts: artima.com/lejava/articles/designprinciples4.html \$\endgroup\$ Commented Apr 8, 2015 at 5:19
  • \$\begingroup\$ About the i variable. I happen to know that it is faster to do two get(i) on an array list, than it is to do the sublist of an ArrayList. If you were to be using a linked list, I would change the strategy. But, I did not think this was a hugely significant thing. sublist is relatively seldom used, and can cause confusion. \$\endgroup\$ Commented Apr 8, 2015 at 5:20
  • \$\begingroup\$ Also see wikipedia: Composition over inheritance \$\endgroup\$ Commented Apr 8, 2015 at 5:30

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.