1
\$\begingroup\$

Find the fractions for, all positive numbers, all negative numbers and zeros in a given set of integer array.

I wrote the below program. If you see any improvements required, please feel free to point them out.

Sample input: 
6
-4 3 -9 0 4 1

Sample output:
0.500000
0.333333
0.166667

Here is my program :)

public class Solution {

    public static void main(String[] args) {

        Scanner scan = new Scanner(System.in);
        int size = scan.nextInt();
        scan.nextLine();

        String s = scan.nextLine();
        List<Double> doubleList = Arrays.stream(s.split(" ")).mapToDouble(Double::valueOf).collect(ArrayList<Double>::new, List::add, List::addAll);

        int postive = 0;
        int negative = 0;
        int zeros = 0;
        double total = (int) doubleList.stream().count();

        DoublePredicate filterPositives = d -> d > 0;
        DoublePredicate filterNegatives = d -> d < 0;
        DoublePredicate filterZeros = d -> d == 0;

        postive = countNumbers(doubleList.stream().mapToDouble(Double::valueOf), filterPositives);
        negative = countNumbers(doubleList.stream().mapToDouble(Double::valueOf), filterNegatives);
        zeros = countNumbers(doubleList.stream().mapToDouble(Double::valueOf), filterZeros);

        DecimalFormat df = new DecimalFormat("#.000000");
        System.out.println(df.format(postive/total));
        System.out.println(df.format(negative/total));
        System.out.println(df.format(zeros/total));
    }

    public static int countNumbers(DoubleStream ds, DoublePredicate fd){
        return (int) ds.filter(fd).count();
    }

}
\$\endgroup\$
3
  • \$\begingroup\$ You are reading 3 times the list. Using the stream methods is good but iterating it yourself would give you the opportunities to read only once by incrementing the variable depending on the value. \$\endgroup\$
    – AxelH
    Sep 2 '16 at 9:48
  • \$\begingroup\$ Once a stream consumed, we can't reuse it again. That's why I had to read the List (doubleList) 3 times. \$\endgroup\$ Sep 2 '16 at 9:57
  • \$\begingroup\$ That not the problem, a Predicate won't let you do the 3 checks in one call, you need to do it for each condition, so the list is iterated 3 times, one per condition. Not bad for small list but for a huge one you are going to wait the result. If you iterate yourself the list, you will save 2/3 of the time (roughly), using a switch and compare method. \$\endgroup\$
    – AxelH
    Sep 2 '16 at 10:02
1
\$\begingroup\$

Using the predicates in your case could be expensive for big list since you use 3 meaning 3 complete iteration on the list.

Here is a small update of you code (I can't use the Java 8 API since I only have the Java 6 at the office ...) to do the same check but without predicate (or if else)

    Scanner scan = new Scanner(System.in);
    int size = scan.nextInt();
    scan.nextLine();

    int[] sum = new int[3];

    String s = scan.nextLine();
    String[] array = s.split(" ");

    double total = array.length;
    for(String d : array ){ //Here, you shoud use your list instead of my array
        sum[Double.compare(Double.parseDouble(d), 0) + 1]++; 
        //compare will return -1 | 0 | 1, adding one to increment the correct cell in the array.
    }

    DecimalFormat df = new DecimalFormat("#.000000");
    System.out.println(df.format(sum[2]/total));
    System.out.println(df.format(sum[0]/total));
    System.out.println(df.format(sum[1]/total));

PS : I could update the code to match your List but to prevent any typo, I prefer to show you the logic I used with the API I can use.

\$\endgroup\$
1
\$\begingroup\$

Your requirements says:

Find the fractions for, all positive numbers, all negative numbers and zeros in a given set of integer array.

You are reading doubles instead of integers. You should read integers instead.

You are using different streams to get a different piece of information each time. For big inputs, this could be a performance problem.

You can iterate over the int array and collect all the data you need in a for loop. The code would be very straightforward and iterate the input only once:

    int positive = 0;
    int negative = 0;
    int zero = 0;
    for (int n : arr) {
        if (n > 0) {
            positive++;
        } else if (n < 0) {
            negative++;
        } else {
            zero++;
        }
    }
    System.out.println(df.format(positive / (double) arr.length));
    System.out.println(df.format(negative / (double) arr.length));
    System.out.println(df.format(zero / (double) arr.length));  

If you want to use the stream API for this task, you probably need to try to get all the data using only one stream.

This is a possible implementation, the numbers are being grouped in three groups (Positive, Negative and Zero) and getting the counts of each group. There are also three constants defined to use as the group keys.

    final Integer POSITIVE_KEY = 1;
    final Integer NEGATIVE_KEY = -1;
    final Integer ZERO_KEY = 0;
    ...
    String s = scan.nextLine();


    Map<Integer, Long> result = Arrays.stream(s.split(" "))
            .map(Integer::valueOf)
            .collect(Collectors.groupingBy(i->i > 0 ? POSITIVE_KEY : i < 0 ? NEGATIVE_KEY : ZERO_KEY,
                                        Collectors.counting()));
    double total = (double) result.values().stream().mapToLong(l->l).sum();
    System.out.println(df.format(result.getOrDefault(POSITIVE_KEY, 0l) / total));
    System.out.println(df.format(result.getOrDefault(NEGATIVE_KEY, 0l) / total));
    System.out.println(df.format(result.getOrDefault(ZERO_KEY, 0l) / total));
\$\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.