1

I am having trouble trying to write a method to return the object that has the least volume in a generic ArrayList. These are the guidelines I was given to write the code:

min() - This method takes an ArrayList of Bounded Generic Type which only allows Shape objects and its subclasses. - The method should return the object with the minimum volume from the list of objects.

But I'm not entirely sure if I even followed it right. Is there a way I could use the Collections.min (and Collections.max since I have to write a max volume method too)? I get a bound mismatch error saying: The generic method min(Collection) of type Collections is not applicable for the arguments (ArrayList). The inferred type Shape is not a valid substitute for the bounded parameter >

My Shape class is simply an interface with a getVolume(); method where my other classes (Spheres, Ellipsoids, etc.) override this method:

public interface Shape {
    public double getVolume();
}

And here is my min method (in another class with other methods) I'm having problems with:

public static <T> T  min() {
    ArrayList<? extends Shape> list;    

     T min = Collections.min(list));
        return min;
2
  • 1
    Your Shape interface must extend Comparable<Shape>, or you must pass a Comparator<Shape> to Collections.min() (that would be my preferred solution). Commented Apr 17, 2014 at 6:10
  • Voted your comment up, you're right, but if it isn't possible, you still need to implement a compareTo() method of some sort. here's the doc for comparable docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html Commented Apr 17, 2014 at 6:14

5 Answers 5

1

It's because Java erases type at runtime, so the Collection doesn't know what type it's actually dealing with. It's a limiting factor when using Java generics - I've bumped heads with it before a few years ago but I couldn't figure out a way to get around it and it turned out to be a language restriction.

The best thing to do is create a public T getMinVolume(ArrayList<T> list) method to iterate through each T.

eg.

public T getMinVolume(ArrayList<T> list) {
    T min = null;
    for(T item: list) {
        if (min == null) {
            min = item;
        }

        if (min > item) {
            min = item;
        }
    }

    return min;
}

Something like that, my Java is a bit rusty but the logic should work.

Sign up to request clarification or add additional context in comments.

Comments

1

To compare the volumes, you can make Shape Comparable or use a Comparator.

Comparable

This requires changes to all shapes, but none to the code using them. I used an abstract class to easily put the comparing to all classes.

public interface Shape extends Comparable<Shape> {
    public double getVolume();
}

public abstract class BaseShape implements Shape {
    public int compareTo(Shape other) {
        return Double.compare(getVolume(), other.getVolume());
    }
} 

public class Box extends BaseShape {
    public double getVolume() {
        return volume;
    } 
} 
public class Ball extends BaseShape { /* ... */ } 

And to use:

Collections.min(collection);

Comparator

This needs no modification to the shapes, but a bit more code to use them.

public class ShapeComparator implements Comparator<Shape> {
    public int compare(Shape a, Shape b) {
        return Double.compare(a.getVolume(), b.getVolume());
    } 
} 

And to use:

Collections.min(collection, new ShapeComparator());

Comments

1

There are two options in java.utils.Collections.

  • static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
  • static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)

The first of these requires your Shape to implement Comparator<Shape>

class Shape implements Comparable<Shape> {
    int compareTo(Shape other) {
       return Math.signum(getVolume()-other.getVolume);
    }
    ...
}

ArrayList<Shape> myShapes = ...
Shape minShape = Collections.min(myShapes);

The second requires you to create a custom comparator:

class Shape {
    ...
}

class ShapeVolumeComparator implements Comparator<Shape> {
    int compare(Shape s1, Shapes2) {
       return Math.signum(s1.getVolume()-s2.getVolume());
    }
}

ArrayList<Shape> myShapes = ...;
Shape minShape = Collections.min(myShapes, new ShapeVolumeComparator() );

The first is less code, but the second is more adaptable if you want to sort on something else - say surface area, or position.

Comments

0
public <E extends Comparable> E getMin(List<E> list){
    E min = null;
    for(E element:list){
        if(min == null){
            min = element;
            continue;
        }
        if(element.compareTo(min) < 0){
            min = element;
        }
    }
    return min;
}

Comments

-1

You should this method http://www.tutorialspoint.com/java/util/collections_min_comparator.htm and provide a comparator :

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;

public abstract class Shape {
    public abstract double getVolume();

    public Shape min(Collection<? extends Shape> col) {
        return Collections.min(col, new Comparator<Shape> () {
            public int compare(Shape l, Shape r) {
                return ((Double) l.getVolume()).compareTo(r.getVolume());
            }
        });
    }
}

Comments

Your Answer

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.