3

Given an array data[], a minValue, and maxValue, create a method that returns an array comprised solely of values from data[] between minValue (inclusive and maxValue (exclusive).

I actually did the problem, but I'm wondering if there's a cleaner (less noob) way to do it. To get the size of my temp[] i actually do the full loop, which worked but seems clunky.

public static int[] superSweetFilterMethod(int[] data, int minValue, int maxValue) {
    int j = 0;
    for (int i =0; i < data.length ; i++) {
      if (data[i] >= minValue && data[i] < maxValue) {
        j++;
      }
    }
    int k = 0;
    int[] temp = new int[j];
    for (int i =0; i < data.length ; i++){
      if(data[i] >= minValue&& data[i] < maxValue){
        temp[k] = data[i];
        k++;
      }
    }
    return temp;
  }

Like I said, this works, I'd just like to git gud. Thanks for the lesson, Wise Ones.

Monjoie11
  • 33
  • 5
  • Your code is optimal. There’s no avoiding 2 passes given you are returning an array. A linked list would require only 1 pass, but would probably perform worse. – Bohemian Jun 15 '19 at 23:53
  • 2
    You could use `Arraylist` and push the `data[i]` dynamically into it without needing to know the size of `temp[]`. And if caller method cannot be modified to accept `ArrayList`, convert `ArrayList` to `Array` just before return(`tempList.toArray()`) – Sagar Chilukuri Jun 15 '19 at 23:58
  • Thanks dude! Will read up on ArrayList and play around with it. – Monjoie11 Jun 16 '19 at 00:18

2 Answers2

2

You can avoid looping twice with the same if clause by initializing the temp array at the same size of your original array, and use a counter int to track the number of elements in your temp array.

But then, before returning your temp array, you still need to reduce it in size. This can be done by using utility method Arrays.copyOfRange. (which is still a second loop internally, but without repeating your if clause)

I like @Sagar Ch's solution (comment) better (ArrayList), because it is more readable, and it tracks the number of elements for you.

Stijn Haezebrouck
  • 387
  • 1
  • 2
  • 14
  • Thanks dude! I had tried initializing at the same size and got extra zeros. Could've done copyOfRange to pull it out. Shaolin bow. Back to it. – Monjoie11 Jun 16 '19 at 00:20
2

As others said, given the constraints of using arrays and primitives, your code looks good, efficient while requiring two passes. If you can choose to use objects and collections, your code could be rewritten to be shorter and simpler.

Java collections framework

An array in Java is purposely quite limited in its features. The primary reason for array is to make easier porting of code from other languages such as C. An array can also be useful when (a) working with primitives and (b) compact use of memory is a priority — not the case generally in business-oriented apps.

For day-to-day work within Java, we use the feature-rich Java collections framework bundled with Java or a similar framework from a 3rd-party such as Google Guava.

In Java, the common alternative to array is ArrayList class. This class masks the use of arrays underneath, to give the illusion of an array that is growing or shrinking as needed. For small sized lists, you can simply rely on this auto-sizing.

The collections framework only handles objects, not primitives. But Java will use its auto-boxing feature to automatically promote your int primitive values into Integer objects. Objects take more memory and are a tiny bit slower, but have much more flexible.

Java has a for-each feature to loop an array or collection, to simplify the syntax of a for loop.

Notice how “not less than minimum” is another way of saying “is greater than or equal to minimum”. Same effect, use either as a matter of style.

ArrayList< Integer > numbers = new ArrayList<>() ;
for( int i : data ) {                               // for-each syntax, to loop an array.
    if ( !( i < min ) && ( i < max ) ) {            // Half-Open `[)`, beginning/lower is inclusive while the ending/higher is exclusive.
        numbers.add( i ) ;                          // Auto-boxing converts your `int` primitive to an `Integer` object.
    }                                               // Else discard this number, and move on to the next.
}

Just one loop needed. Much shorter code, easier to read.

You likely will want to return the result as the more general List rather than ArrayList. This is polymorphism in action, if you’ve studied that OOP concept.

public static List< Integer > filterOutTooLowAndTooHigh( int[] ints , int min , int max )
{
    ArrayList< Integer > numbers = new ArrayList<>() ;
    for( int i : ints ) {
        if ( !( i < min ) && ( i < max ) ) { 
            numbers.add( i ) ;
        }
    }
    return numbers ;
}

You might want to sort the returned numbers. The Collections class (notice the plural s) has utility methods for sorting.

Collections.sort( numbers ) ;

ArrayList actually grows by allocating a new array and copying over from the old array. So the new array may be sized larger than you actually need. So when you have finished adding elements to the ArrayList, you may want to trim the underlying array to the minimum size needed.

Collections.trimToSize( numbers ) ;

Lastly, if you have very large arrays, you may want to set the initial size of the ArrayList. This avoids the growing process of allocating a new array and copying over, lather, rinse, repeat. Again do not worry about this for arrays in the dozens, hundreds, or few thousand size unless your app does this work many times.

int initialCapacity = 100_000 ;
ArrayList< Integer > numbers = new ArrayList<>( initialCapacity ) ;

Put this all together.

public static List< Integer > filterOutTooLowAndTooHigh( int[] ints , int min , int max )
{
    int initialCapacity = 100_000 ;
    ArrayList< Integer > numbers = new ArrayList<>( initialCapacity ) ;
    for( int i : ints ) {
        if ( !( i < min ) && ( i < max ) ) {  // Half-Open `[)`, beginning/lower is inclusive while the ending/higher is exclusive.
            numbers.add( i ) ;
        }
    }
    Collections.sort( numbers ) ;
    Collections.trimToSize() ;
    return numbers ;
}

I have not tested that code, but should get you going in the right direction.

You likely could do this in even less code by using Java Streams. But as a beginner, you should not use streams until later. First get comfortable with working with arrays and collections manually as discussed here.

By the way, this might help you: Resize an Array while keeping current elements in Java?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154