5

I have an array of Strings

String[] suffixes = getSuffixes();

and I want a new array based on the old one with a prefix appended. I'd do something like this in Ruby, JavaScript, Perl, Lisp, and the like:

// pseudocode for what I would like

String[] full_words = suffixes.map(suffix => "re" + suffix);

I'm limited to Arrays in my application. (This is a small part of an assignment for school and I'm not allowed to use the Collections library.) Any way to do this in Java 8?

6
  • 1
    what is wrong with a good ol' fashioned loop? Commented Sep 20, 2018 at 1:47
  • @ScaryWombat After looking at way too many for loops where a map would be more appropriate, something inside me recoils at the very thought of typing one more for... :) Mapping over a list feels better. It's terser, easier to read, etc. (I come from a Lisp/Scheme background.) Commented Sep 20, 2018 at 1:55
  • Ashton, I hear where you are coming from, but it is not me that is imposing restrictions on your code, and if it was me, I would never using an array in the first place. Commented Sep 20, 2018 at 1:58
  • Yeah, me neither. Heck, I'd never use Java in the first place. :-D A big part of this is curiosity. Commented Sep 20, 2018 at 2:00
  • 1
    It's still fast enough for what I need it for! :) I do appreciate the comments on CPU overhead. I'll keep that in mind if I'm ever writing production Java. However, this is a school project, and I'm trying to explore the language. Commented Sep 20, 2018 at 2:05

1 Answer 1

4
String [] full_suffixes = Arrays.stream(suffixes)
                          .map(suffix -> "re" + suffix)
                          .toArray(size -> new String[size])

"ol' fashioned" loop is too verbose:

String [] full_suffixes = new String[suffixes.length];
for(int i = 0; i < suffixes.length; i++) {
    full_suffixes[i] = "re" + suffixes[i];
}

To address the "much too fast" comment:

import java.util.Arrays;

public class Test {
    static int N = 10000;
    public static void main(String [] args) throws Exception {
        if(args.length > 0) {
            N = Integer.parseInt(args[0]);
        }
        String [] array = new String[100000];
        for(int i = 0; i < array.length; i++) {
            array[i] = "abcdef" + i;
        }
        long start = System.currentTimeMillis();
        fancy(array);
        System.err.println("Fancy took " + (System.currentTimeMillis() - start) + "ms");
        start = System.currentTimeMillis();
        oldSchool(array);
        System.err.println("Loop took " + (System.currentTimeMillis() - start) + "ms");

    }

    public static void fancy(String [] array) {
        for(int i = 0; i < N; i++) {
            String [] full_suffixes = Arrays.stream(array)
                                          .map(suffix -> "re" + suffix)
                                          .toArray(size -> new String[size]);
        }
    }

    public static void oldSchool(String [] array) {
        for(int i = 0; i < N; i++) {
            String [] full_suffixes = new String[array.length];
            for(int j = 0; j < array.length; j++) {
                    full_suffixes[j] = "re" + array[j];
            }
        }
    }
}

Running (jdk 1.8.0_60):

$ java Test 100
Fancy took 502ms
Loop took 398ms
$ java Test 1000
Fancy took 3067ms
Loop took 2227ms
$ java Test 5000
Fancy took 13288ms
Loop took 11494ms
$ java Test 10000
Fancy took 27744ms
Loop took 27446ms

I would not call this much faster, but yeah, unfortunately there is a trade-off between performance and code clarity in the current implementation of streaming framework.

Edit: To address the discussion about map vs for loops - it is not about hte number of lines or characters to type, maybe I'm also spoilt by languages with higher-order functions, but for me there is a mental disconnect between transforming a collection (which is simply what map does) and putting my brain into the details of its size and how to get/assign elements. Also it is worth to mention that Java streaming framework is still ages behind.

Sign up to request clarification or add additional context in comments.

4 Comments

Not to mention how the "ol' fashioned" loop is much too fast, as well. Never do the oldy-moldy simple and high-performing thing when there's a hot sexy new dead-dog slow solution available!
.map("re"::concat).toArray(String[]::new)
In light of your evangelism on behalf of the stream, @khachik, I'm willing to admit I've been perhaps a bit too harsh in my criticism. Streams are slower, yes, but maybe not that much slower. I shall try to hate them a little less going forward, but I refuse to give up on the "good ol` loop" as my measure of first resort.
@KevinAnderson God save me from evangelizing anything, especially Java-related.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.