11

I'm learning how to use stream, and I get a problem with this method.

public static String[] inArray(String[] array1, String[] array2) {
   return Arrays.stream(array1)
  .filter(str -> Arrays.stream(array2).anyMatch(s -> s.contains(str)))
  .distinct().sorted().toArray(**String[]::new**);
}

I'm so confused about String[]::new, could you give me a hint?

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
xshe2
  • 147
  • 1
  • 1
  • 6

10 Answers10

14

String[]::new means size -> new String[size].

When Stream#toArray(IntFunction<A[]> generator) is ready to produce an array, it calls generator and passes (generator.apply) the size of the inner collection to get a collection to fill it up.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
8

I would say the existing answers provide some insight but none of them yet talk about IntFunction<R>.

To add to them explain, what it means in the context of Stream.toArray(String[]::new) is that it represents an IntFunction implementation such as :

new IntFunction<String[]>() {
    @Override
    public String[] apply(int value) {
        return new String[value];
    }
}

where the code creates a newly allocated String[] of size value and produces the array of that size as an output.

Naman
  • 27,789
  • 26
  • 218
  • 353
6

You are right to be confused, because Java isn't really super clear about types vs. classes.

We know that String[] is a type, as you can declare variables of that type:

jshell> String[] s = new String[]{"Hello", "world"}
s ==> String[2] { "Hello", "world" }

However, String[] actually is treated as a class in Java and not just a type:

jshell> s.getClass()
$2 ==> class [Ljava.lang.String;

That funny looking [Ljava.lang.String, representing the type "array of string" shows up in response to the getClass invocation. I agree that it is surprising. But every object in Java has to have a class, and String[] is that class. (In other languages, you might see something like Array<String> which might be a dash clearer. But then Java has type erasure so again, things look a little confusing.)

In your particular case, here's what's going on. You need to be careful with types when making arrays from streams. Naively, you might get:

jshell> Arrays.asList("a", "b").stream().toArray()
$5 ==> Object[2] { "a", "b" }

So we want the version of toArray that gives us an array:

jshell> Arrays.asList("a", "b").stream().toArray((n) -> new String[n])
$7 ==> String[2] { "a", "b" }

That's better! The result type is an array of strings, instead of just an array of obejcts. Now the (n)->new String[n] can be replaced with a method reference for construction. Java allows array types in method references! So we can write:

jshell> Arrays.asList("a", "b").stream().toArray(String[]::new)
$8 ==> String[2] { "a", "b" }

Aside: There are some caveats when using array types in method references like this, such as the requirement that the array type must be reifiable, but I think that's a little beyond what you might have been asking. The TL;DR here is that, by design, Java allows array types in (constructor-like) method references with ::new.

Ray Toal
  • 86,166
  • 18
  • 182
  • 232
5

This is a method reference expression see JLS 15.13. The syntax for method references is:

MethodReference:
    ExpressionName :: [TypeArguments] Identifier
    Primary :: [TypeArguments] Identifier
    ReferenceType :: [TypeArguments] Identifier
    super :: [TypeArguments] Identifier
    TypeName . super :: [TypeArguments] Identifier
    ClassType :: [TypeArguments] new
    ArrayType :: new 

The particular case you are looking at is the last one. In your example, String[] is an ArrayType which means that it consists of a type name followed by one or more [].

There shouldn't be a class named String[] which is very lame and I could not interpret what it is actually meant for.

See above: it is a type specification not a class name. From a syntactic / linguistic perspective, this usage is analogous to:

  Class<?> c = String[].class;

or

  if (a instanceof String[])

or even

  public void myMethod(String[] arg)

(You wouldn't call those "lame" ... would you?)


Now you could have a valid case for saying that it is syntactically unexpected (especially to a pre-Java 8 programmer) to be able to use the new keyword like this. But this unexpected syntax is a consequence of the strong imperative that the designers have to NOT break backwards compatibility when adding new language features to Java. And it is not unintuitive. (At least, I don't think so. When I first saw this construct, is was obvious to me what it meant.)

Now, if they were starting with a clean slate in 2018, a lot of details of the Java language design would be simpler and cleaner. But they don't have the luxury of doing that.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
3

The documentation of Stream#toArray says it exactly:

The generator function takes an integer, which is the size of the desired array, and produces an array of the desired size.

for example:

IntFunction<int[]> factory = int[]::new;
//                     v--- once `apply(3)` is invoked,it delegates to `new int[3]`
int [] array = factory.apply(3);
//       ^--- [0, 0, 0] create an int array with size 3

String[]::new is a method reference expression and it must be assigned/casted to a certain functional interface type at compile time:

A method reference expression is used to refer to the invocation of a method without actually performing the invocation. Certain forms of method reference expression also allow class instance creation (§15.9) or array creation (§15.10) to be treated as if it were a method invocation.

A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.

Edit

As @Eugene mentioned in comments below. It's necessary to let you know how and where the stream create an fixed size array to collecting all elements.

The following table is showing the stream how to calculates the array size:

The following table is showing the stream where to creates a fixed size array by array generator IntFunction:

Community
  • 1
  • 1
holi-java
  • 29,655
  • 7
  • 72
  • 83
  • 2
    if you *highlight* that this is actually `size -> new String[size]`, it would make it instantly clear IMO. plus one – Eugene Jun 01 '17 at 20:00
  • @Eugene yes, sir. but others has already clarified. I only want to clarify **what** and **how** to use a method reference expression. because the OP confused about it. – holi-java Jun 01 '17 at 20:02
  • 2
    well if you are on that path, you could say where that `size` comes from in `String[]::new`... especially since the OP uses `filter` that would break the `SIZED` property of the Spliterator. But that would be way too much probably as he says : `I'm learning how to use stream` – Eugene Jun 01 '17 at 20:05
  • @Eugene good idea. could I add it in my answer simply to clarify how a `String[]` created with a special size? – holi-java Jun 01 '17 at 20:20
  • @Eugene Thanks for taking the time to help me. – holi-java Jun 01 '17 at 20:33
  • @Eugene Excuse me, sir. it's too late last night. How about this now? – holi-java Jun 02 '17 at 13:19
  • 1
    yes and no :) what I meant to say is that if there are no `filter` or `flatmap` operations - that do change the size of the Stream - then it is obvious. On the other hand if intermediate operations do change it's size, it uses `SpinedBuffer` and then creates an array from that... – Eugene Jun 02 '17 at 13:22
  • @Eugene :)..., maybe I think about it too more. – holi-java Jun 02 '17 at 13:23
  • 3
    it's called "overthinking", yes. – Eugene Jun 02 '17 at 13:24
3
String[]::new

This is lambda for the following method:

public String[] create(int size) {
    return new String[size];
}
Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35
  • 4
    It is a `method reference` and is not really referring to the method you're presenting.. – Yassin Hajaj May 26 '19 at 02:45
  • 4
    Strictly speaking it is not a lambda, and won't be equal to that lambda. But it is certainly *functionally equivalent* to that method, or a lambda that does the same thing as that method.. – Stephen C May 26 '19 at 02:49
  • 4
    Combining the two excellent comments, one might say "it is a method reference that is functionally equivalent to the lambda `(size)->new String[size]` :) – Ray Toal May 26 '19 at 07:43
1

Your whole stream operation is terminating converting that into an array, that is what you do with the last method toArray(), but an array of what?.... of Strings ( thus String[]::new)

ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
1

The parameter of toArray(...) is a Functional Interface (namely IntFunction<R> and then String[]::new is defined as the Method Reference or in that case constructor to use that generates an array of the desired type.

See https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html

And https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Demogorii
  • 656
  • 5
  • 16
0

Adding to the answer of Andrew Tobilko:

"String[]::new means size -> new String[size]"

which, since toArray takes an IntFunction, is similar to:

IntFunction<String[]> generator = new IntFunction<String[]>() {
    @Override
    public String[] apply(int size) {
        return new String[size];
    }
};
user16320675
  • 135
  • 1
  • 3
  • 9
-1

To convert your stream to another List, you can use:

.collect(Collectors.toList());
melli-182
  • 1,216
  • 3
  • 16
  • 29