1

I have a method that returns a count of the objects that have a higher value than the average of all the objects within an ArrayList.

The 'User' object has the integer value stored within it's class (level).

My method works but I'm wondering if there's a better way of finding the average of all object values?

public int bestPlayers() {

    ArrayList<User> players = new ArrayList<User>();
    int bestPlayers = 0;
    int totalPlayerLevel = 0;
    double averageLevel = 0;

    for (int i = 0; i < players.size(); i++) {
        totalPlayerLevel += players.get(i).level;
    }

    averageLevel = totalPlayerLevel / players.size();

    for (int i = 0; i < players.size(); i++) {
        if (players.get(i).level > averageLevel) {
            bestPlayers++;
        }
    }

    return bestPlayers;
}
4castle
  • 32,613
  • 11
  • 69
  • 106
BT93
  • 329
  • 2
  • 9
  • 25
  • 4
    `averageLevel = (double)totalPlayerLevel / players.size;` for better precision. – MikeCAT May 15 '16 at 06:47
  • http://stackoverflow.com/questions/12002332/how-to-manipulate-arrays-find-the-average-beginner-java – Idos May 15 '16 at 06:48
  • `averageLevel = (double)totalPlayerLevel / players.size();` might be better because the property `java.util.ArrayList.size` is private. – MikeCAT May 15 '16 at 06:49
  • Is there a reason why `level` is `public`? – Maroun May 15 '16 at 06:49
  • @MikeCAT Actually the other way. Change `averageLevel` to `int`, since you're going to compare it to the `int level` field anyway. And since it's `>`, not `>=`, the truncation from integer division is ok. – Andreas May 15 '16 at 06:50
  • rks, to answer your question: You could do things shorter with streams in Java 8, but for non-stream version, that's pretty much how you do it. Of course, I assume there's code somewhere to actually fill the `players` list, right? ;-) – Andreas May 15 '16 at 06:52
  • @MarounMaroun There's no reason it's public, I have changed `averageLevel` to `int` instead of casting `totalPlayerLevel` and used the getter for size. – BT93 May 15 '16 at 06:54
  • @Andreas I have an inOrderTraversal method that fills the players list with the objects from a binary tree. I have used streams before to find the max value but didn't think to use it to find the average! – BT93 May 15 '16 at 06:56

2 Answers2

6

Java 8 provides a better way, using IntStream#average():

double average = players.stream()
                        .mapToInt(p -> p.level)
                        .average()
                        .orElse(0);

Then to output the number of "above average" players:

return (int) players.stream()
                    .filter(p -> p.level > average)
                    .count();
4castle
  • 32,613
  • 11
  • 69
  • 106
  • If you want to show a stream way of doing it, show the second half too, i.e. counting the "best players". Right now it's an incomplete answer. – Andreas May 15 '16 at 06:59
  • @Andreas The question only asked about a better way to find the average, but I have updated. – 4castle May 15 '16 at 07:00
  • No need to `mapToInt()`. Just do `filter(p -> p.level > average)`. – Andreas May 15 '16 at 07:02
2

Your code works in O(n), since you travel through array twice. Well, since you have to calculate average, that means you have to travel at least once, so there is no option for performance faster then O(n).

Second thing you do, you must count players with level higher then average. Only speedup here could be if you have sorted array (have before, not calculating now, since its O(nlogn), then you can find first one with higher level then average and calculate number of the rest. That would cost O(logn), but its performance is still O(n), since you calculated average.

Adnan Isajbegovic
  • 2,227
  • 17
  • 27