0
\$\begingroup\$

I'm curious if there is another way to code this. Could it be done with a while loop for instance?

public static int numUnique(double[] list) {
    int uniques = 0;
    if(list.length == 0){return 0;}
    for(int i = 0; i < list.length; i++){
        int dups = 0;
        for(int j = i + 1; j < list.length; j++){
            if(list[i] == list[j]){
                dups = dups + 1;
            }
        }
        if(dups == 0){
            uniques = uniques + 1;
        }
    }
    return uniques;
}
\$\endgroup\$
  • 3
    \$\begingroup\$ Welcome to Code Review. Does your code work as intended, if it does , can you tell us what you are trying to achieve with this code via a summary in your post. \$\endgroup\$ – Siobhan Nov 19 '16 at 17:11
3
\$\begingroup\$

No need for any loops with Java 8 streams.

  1. Get a DoubleStream from the array with Arrays.stream(double[] array) so that you can
  2. get the distinct() elements from that
  3. which you can count() to retrieve your result.
public static long numUnique(double[] list) 
{
    return Arrays.stream(list).distinct().count();
}

As count() returns a long, I modified the signature of your method to return long, too. It's within the responsibility of the user of your method then to perform any cast on the result.

Full example program:

import java.util.Arrays;

public class UniqueDoubles
{
    public static void main(String[] args)
    {
        double[] doubles = {1, 2, 3.5, 3.5, 2, 345345.345345};

        System.out.println(numUnique(doubles));
    }

    public static long numUnique(double[] list) 
    {
        return Arrays.stream(list).distinct().count();
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Neat! I am still learning this Java 8 stuff. It works on primitive arrays too? I will plan to take a closer look at your approach very soon. +1 \$\endgroup\$ – Phil Freihofner Nov 20 '16 at 3:38
2
\$\begingroup\$

An issue with this code is actually the equal test of double (floating point numbers).

       if (list[i] == list[j]) ...

It is more common to test almost equal.

       if (Math.abs(list[i] - list[j]) < EPSILON) ...
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Seems to me the simplest thing would be to put all the doubles into a HashSet and get its length. A Set does not allow duplicate values.

    public static int numUnique(double[] list) 
    {
        Set<Double> hashSet = new HashSet<Double>();

        for (double d: list)
            hashSet.add(d);

        return hashSet.size());
    }

[EDIT: code revised to match structure of OP's function. For loop changed to for-each]

It would be simpler if we could use the hashSet.addAll() method, but that would only work if the "list" array was Double[], not double[]. By going one-by-one (in the for loop) autoboxing of the double to Double occurs.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ That's quite an indirect approach. While it may work, it would definitely make it difficult for the reader to understand what you are doing. In addition, you are duplicating the data into memory - once in the original array and then into the HastSet. Imagine what happens if you have a million items in the array. -1 \$\endgroup\$ – Nisarg Nov 20 '16 at 6:05
  • \$\begingroup\$ Strongly disagree. This is a well known technique. HashSet specifically gives up ordering and iteration for uniqueness of members. Second objection, about duplicate data: the hashSet is immediately discarded. There is no duplicate data to maintain. Third, a million items = big so what. We are iterating through the items once. That is the same thing other solutions do. \$\endgroup\$ – Phil Freihofner Nov 20 '16 at 6:07
  • \$\begingroup\$ The streaming answer is likely superior, but I haven't had a chance to get acquainted with the underlying efficiency of this code yet, so I can't say for sure. But I did give it a vote. However, the technique I used is very common, there is no maybe about it working or not. This is a valid answer and a definite improvement over the original code. \$\endgroup\$ – Phil Freihofner Nov 20 '16 at 6:11
  • \$\begingroup\$ 1. I agree that your code is simpler (to my eyes), but at the same time it does have the potential to confuse a reader. Personally I have never seen HashSet used to get distinct members in an array. 2. No doubt the array will be discarded, but why depend on the garbage collector?? I'm coming from C# world, so I'm not sure how efficient GC is with Java. But I would certainly be concerned about your approach if it were C#. \$\endgroup\$ – Nisarg Nov 20 '16 at 6:19
  • \$\begingroup\$ I don't know much about C#. The HashSet is created locally and becomes eligible for GC three lines later when the function exits. No other means for getting into this data area exist. I vouch for this code being quite safe. If one knows that a Set's "super power" is enforcing uniqueness of members, and sees the set being populated and discarded after returning a size, I think that is reasonably clear. If you are not familiar with the Set Interface in Java, I could see where this might raise questions. Does C# have Set? "Set" and "List" are two Interfaces learned fairly early in Java. \$\endgroup\$ – Phil Freihofner Nov 20 '16 at 6:35
0
\$\begingroup\$

Instead of doing \$\frac{n \times (n-1)}{2}\$ comparisons, you should just:

  • make a copy of the array (use the clone method)
  • sort this array (see the java.util.Arrays class)
  • compare each element of the sorted array to its immediate neighbor

By using this idea, the code gets faster, especially for large arrays, since it only needs \$ n \times \log n \$ comparisons for sorting and \$n-1\$ comparisons for detecting the unique values. It does an additional memory allocation, though, but in most cases you don't need to care about this.

Another way, when code readability is more important than execution speed, is to just use Guava's Multiset<Double> and add all the numbers to it. Then count the number of elements for which .count() is 1.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.