2

I want to iterate just the half of an array in java. Is there any elegant way to shorten this up, eg with a for-each loop?

int[] array = {0,1,2,3,4,5};

for (int i = 0; i<array.length/2; i++)
{
    System.out.println(array[i]);
}
Devas
  • 1,544
  • 4
  • 23
  • 28
Niks
  • 35
  • 6
  • Are you having problems with that code? – f1sh Oct 09 '16 at 18:12
  • 1
    no, this is perfect , just go with it but just remove `0+` – Pavneet_Singh Oct 09 '16 at 18:13
  • 5
    You could drop the `0+` part - it doesn't add anything useful. – Mureinik Oct 09 '16 at 18:13
  • Well, the "0" doesn't need to be there. – markspace Oct 09 '16 at 18:13
  • 2
    This code is perfect apart from that unwanted 0 that you are adding to array.length. – vatsal mevada Oct 09 '16 at 18:14
  • 1
    The code is fine and shows the intention. But if you want you can get a (half) [range of the array](https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#copyOfRange(int[],%20int,%20int)) and then do a `for(Integer i:array)` – PeterMmm Oct 09 '16 at 18:15
  • Elegant doesn't necessarily mean short. The goal is to make your code readable and obvious. This satisfies that metric. – Louis Wasserman Oct 09 '16 at 18:20
  • With a foreach, you *usually* go through all the elements of a collection; you can get out of it with a break, or just get a different collection (like the range @PeterMmm suggests). – okaram Oct 09 '16 at 18:23
  • It generally neater and still a little bit more efficient (i believe although compiler may optimize it out) to do your calculation and attr lookup outside of the loop (ie int range = array.length / 2; ) That will make it more readable I think. – Sam Redway Oct 09 '16 at 18:35
  • Apart from using `i – Marco13 Oct 09 '16 at 18:59

3 Answers3

4

If you converted the array into a list using the asList method of the Arrays class in Java, then you can use the forEach method in the List class in Java to print out each element of the list in one single line,

Arrays.asList(array).forEach(System.out::println);

To print only half the array, I'd suggest copying half the array into a new array using the copyOfRange method,

Integer[] newArray = Arrays.copyOfRange(array, 0, array.length/2);
Arrays.asList(newArray).forEach(System.out::println);

EDIT: Like Marko Topolnik pointed out, we're actually starting out with an array of primitive types instead of object types, so in order to use the asList method we're going to have to convert the array into an array of objects (from int to Integer using Integer[] integerArray = ArrayUtils.toObject(array);). However this just seems tedious/inefficient and OP asked for a shorter way so my suggestion would be to use Marko's method,

Arrays.stream(array).limit(array.length/2).forEach(System.ou‌​t::println);

EDIT 2: Like Amber Beriwal pointed out, it should be noted that although the one-line solution above looks pretty due to its conciseness, it is still very inefficient/slow compared to the OP's original method. Therefore, I would like to reiterate Amber's comments that the OP and others should just stick with the original for-loop.

for (int i = 0; i < array.length/2; i++)
{
    System.out.println(array[i]);
}
Chris Gong
  • 8,031
  • 4
  • 30
  • 51
  • @MarkoTopolnik my mistake lemme modify my answer – Chris Gong Oct 09 '16 at 18:39
  • Actually it will print something like `[I@343af45;` because it's a primitive array and will be turned into a single-member `List`. – Marko Topolnik Oct 09 '16 at 18:40
  • Use `Arrays.stream(array).limit(array.length/2).forEach(System.out::println);` – Marko Topolnik Oct 09 '16 at 18:44
  • It will be around 15-20 times slower than the code shared by the `niks`. – Amber Beriwal Oct 09 '16 at 18:47
  • @AmberBeriwal although this solution is inefficient, the OP's main concern was a shorter rather than faster way of writing a for-loop. – Chris Gong Oct 09 '16 at 18:48
  • @ChrisGong He might not be that much experienced thus giving importance to lines of code over performance & memory. I believe, it's our duty to suggest correct solutions instead of just answering what is asked. – Amber Beriwal Oct 09 '16 at 19:00
  • @Amber Beriwal I understand, which is why I'm warning the OP that the solution above is inefficient. I never said that he should use this in large-scale applications rather that this is just an alternative, albeit not the best one. But thank you, your insight is appreciated. – Chris Gong Oct 09 '16 at 19:02
  • @ChrisGong I missed the edited part while I was typing my solution. Glad to see the inefficiency highlighted there. – Amber Beriwal Oct 09 '16 at 19:04
  • For the record, Chris, there is absolutely no difference in performance here. The botleneck is `System.out.println` in all cases. – Marko Topolnik Oct 09 '16 at 19:53
  • @MarkoTopolnik alright, I'll run some microbenchmarks later tonight and include them in my post then. Thanks for the insight! – Chris Gong Oct 09 '16 at 20:14
1

How about:

IntStream.range(0, array.length / 2).map(i -> array[i]).forEach(System.out::println);

One line, and no array copies.

Broken down:

IntStream.range(0, array.length / 2)    //get the range of numbers 0 - (array length)/2
         .map(i -> array[i])            //map from index to value
         .forEach(System.out::println); //print result
Rogue
  • 11,105
  • 5
  • 45
  • 71
0

The answer you have posted is good. Although, I couldn't find a better way to make it compact keeping the performance same, but performance can be improved. Remember following practices while coding:

  1. Algorithm's memory requirement should be optimum
  2. Algorithm's time i.e. performance should be optimum
  3. Algorithm's complexity should not be too much. For significant gains in 1 & 2, this can be skipped.
  4. Considering 1 & 2, lines of code comes at least priority.

Solution 1: This solution will be 4-5 times slower than your approach, plus Stream will take extra space.

Arrays.stream(array).limit(array.length/2).forEach(System.ou‌​t::println);

Solution 2: This solution is faster than the above code and your code (based on my testing), but Stream will take extra space. Also, it is not compact.

Arrays.stream(array).limit(array.length / 2).forEach(new IntConsumer() {
    @Override
    public void accept(int value) {
        System.out.println(value);
    }
});

Solution 3: As suggested by you.

int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
int limit = array.length / 2;
for (int i = 0; i < limit; i++) {
    System.out.println(array[i]);
}

Recommendation: Don't go over to reduce the LOC at the stake of losing performance and memory. It is better to keep up with the solution that gives you best performance..

Amber Beriwal
  • 1,568
  • 16
  • 30
  • Do you have any hint of a proof for your "100-200 times slower" qualification? Same for the "huge memory requirement"? – Marko Topolnik Oct 09 '16 at 19:30
  • Your solution 2 is nothing but Solution 1 in expanded form. Here you make even more outlandish claims about performance. You really shouldn't go so far in making absolutely baseless claims with no attempt to validate them. – Marko Topolnik Oct 09 '16 at 19:32
  • I just used JMH to measure the performance, it is _identical_ in all cases. – Marko Topolnik Oct 09 '16 at 19:47
  • @MarkoTopolnik Hi..I have tested it on my machine. For simple array, it was taking around 0.3 milliseconds, while for solution 1, it was taking 80-90 ms. Regarding solution 2 and solution 1 difference, Please refer [this post](http://stackoverflow.com/questions/39947622/why-system-outprintln-is-slower-than-anonymous-class-implementation-in-java-8). My system configuration is also given there. – Amber Beriwal Oct 09 '16 at 19:50
  • Step One for you: learn what it means to run microbenchmarks on the JVM. http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java Step Two: learn how to use JMH. http://openjdk.java.net/projects/code-tools/jmh/ – Marko Topolnik Oct 09 '16 at 19:52
  • @MarkoTopolnik I will check both links and investigate further. – Amber Beriwal Oct 09 '16 at 19:54
  • `take the array.length/2 as a local variable so that unnecessary calculations does not happen`---this is also wrong advice, the JIT compiler routinely hoists this stuff outside the loop. – Marko Topolnik Oct 09 '16 at 20:08
  • @MarkoTopolnik I tried JMH. It is giving a score in terms of ops/s. I guess it is operations/second. Indeed, this score would be similar for all 3 algorithms. How can I find out the total averaged seconds/milliseconds taken by each algorithm? – Amber Beriwal Oct 09 '16 at 20:35
  • These are annotations that I used for this test: `@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @OperationsPerInvocation(Measure.ARRAY_SIZE / 2) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @State(Scope.Thread) @Fork(1)` – Marko Topolnik Oct 09 '16 at 20:36
  • @MarkoTopolnik Hi...First, I tried all modes but still units are ops/ns or ns/op. Couldn't find any unit ns or ms. Second, I tried it in batch mode in eclipse itself and results are interesting. Expanded form solution comes to be the fastest (say x), original array solution (~ 1.5 times x) and Compact form solution (~ 6-7 times x) – Amber Beriwal Oct 09 '16 at 20:57
  • Certainly your new claims are an improvement over the initial ones, but still not right. If you measured inside Eclipse, you had it collect the process's output, which made `println()` and even narrower bottleneck, and also a highly unpredictable one. The only reasonable way to isolate the effects of output consumption from the overhead of making the system call is to start from the command line and redirect output to /dev/null. I used `System.err` so i can still see regular JMH output. – Marko Topolnik Oct 10 '16 at 05:56
  • @MarkoTopolnik Thanks. I will investigate it further with suggested method. I am glad you pointed all these mistakes. This conversation has added some great items to my knowledge. – Amber Beriwal Oct 10 '16 at 06:11