4

I need to transform

String[] args = {"--path=C:/log", "--time=hourly"};

into

String[] args = {"--path", "C:/log", "--time", "hourly"};

How can I do this in Java 8, in an elegant way?

List<String> newArgs = Lists.newArrayList();

for (String s : args) {
    String[] split = s.split("=");
    newArgs.add(split[0]);
    newArgs.add(split[1]);
}

String[] strarray = new String[newArgs.size()];
return newArgs.toArray(strarray);
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Mister B
  • 123
  • 2
  • 15

6 Answers6

9
String[] result = Stream.of(args)
        .flatMap(a -> Stream.of(a.split("=")))
        .toArray(String[]::new);
Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
  • 6
    might be just me, but I more like the `.map(x -> x.split("=")).flatMap(Arrays::stream)` still one plus. I guess code speaks louder than words here, everyone was posting lots of wording, and yet this has the least characters and the "most" content.. – Eugene Nov 27 '17 at 15:01
  • 3
    I didn't notice that you had this nice crisp answer around. Although I agree with the guy commenting on my comment ... the real fun starts when people start using = chars within quotes, as part of their arguments ;-) – GhostCat Nov 28 '17 at 08:46
6

The other answers are all fine technically, but the real answer is: don't.

Do not re-invent the wheel. Parsing command line options is actually hard. Whatever you come up with works for the first step, but assuming that we are talking about something that is intended to last, and good enough to attract users - then sooner or later, you spent more and more time on dealing with the options.

Thus: instead of doing any of this yourself (which is of course a bit of fun) accept that parsing command line options is a solved problem. Simply get one of the existing solutions, see here for starters.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 3
    Indeed - fun really starts when you find out you need to accept escaped "=" as part of valid input... – Hulk Nov 28 '17 at 08:43
1

Explanation

As you want to use Java 8 code you may use the Stream API (documentation).

Therefore you should first transform your args array into a Stream<String> using the utility method Arrays#stream (documentation). Then you split the arguments by = using String#split (documentation) and afterwards collect them again into an array using Stream#toArray (documentation).

In order to treat each split value as regular value (and not as nested data) you may want to flatten the Stream. So instead of Stream<String[]> you want to have a flattened structure like Stream<String>. You do so by using the Stream#flatMap method (documentation).


Code

Here is variant using the explained approach:

String[] data = Arrays.stream(args)  // String
    .map(arg -> arg.split("="))      // String[]
    .flatMap(Arrays::stream)         // String
    .toArray(String[]::new);

You can also memorize the pattern beforehand and then use Pattern#splitAsStream (documentation):

Pattern patt = Pattern.compile("=");
List<String> data = Arrays.stream(args)  // String
    .map(patt::splitAsStream)            // String[]
    .flatMap(Arrays::stream)             // String
    .toArray(String[]::new);
Zabuzard
  • 25,064
  • 8
  • 58
  • 82
0

This will produce a List, but you can transform it.

final List<String> parts = Arrays.stream(args)
    .flatMap(argument -> Arrays.stream(argument.split("=")))
    .collect(Collectors.toList());

You might consider argument.split("=", 2).

Gorazd Rebolj
  • 803
  • 6
  • 10
0
    String[] args = {"--path=C:/log", "--time=hourly"};
    args = String.join("=", args).split("=");
    System.out.println(Arrays.toString(args));
Eritrean
  • 15,851
  • 3
  • 22
  • 28
0

I was a little suspicious about the stream performance, but thanks to a comment of Hulk, I realized that you need to warm up the lambda executions (run it at least twice) to get reasonable result.

public class StreamTest {
    public static void main(String[] args) {
        // will include lambda warm up time
        java8test();

        java8test();

        java7test();
    }


    public static void java8test() {
        List<String> test = new ArrayList<>();
        test.add("--path=C:/log");
        test.add("--time=hourly");

        long start = System.nanoTime();
        List<String> result = test.stream().flatMap(e->{
            String[] array = e.split("=");
            return Stream.of(array);
        }).collect(Collectors.toList());

        long end = System.nanoTime();

        System.out.println("Java 8 style: " + (end-start) + "ns: " + result);
    }

    public static void java7test() {
        String[] test2 = {"--path=C:/log", "--time=hourly"};

        long start = System.nanoTime();
        test2 = String.join("=", test2).split("=");
        long end = System.nanoTime();
        System.out.println("old style: " + (end-start) + "ns:" + Arrays.toString(test2));
    }

}

Performance:

Java 8 style: 188154148ns: [--path, C:/log, --time, hourly]
Java 8 style: 129220ns: [--path, C:/log, --time, hourly]
old style: 364275ns:[--path, C:/log, --time, hourly]
Mick
  • 954
  • 7
  • 17
  • This is a very bad benchmark - while there is probably really a significant difference, you are measuring lots of unrelated stuff here (warmup, creation of the test array etc) – Hulk Nov 28 '17 at 08:39
  • Thanks a lot to Hulk! I totally got it wrong. That warmup time has made me trip before.... I will now update my answer for the sake of correctness. – Mick Nov 28 '17 at 10:00