-1

I have a list with some strings and I want to filter these list, but the methode reference ::startsWith is not working as I expect it.

private static final String KEY = "key=";

List<String> keyList = List.of("key=123", "value12", "value=34", "key=5678");

List<String> filtered = keyList.stream().filter(KEY::startsWith).collect(Collectors.toList());
List<String> filtered1 = keyList.stream().filter(e -> e.startsWith(KEY)).collect(Collectors.toList());
List<String> filtered2 = keyList.stream().filter("key="::startsWith).collect(Collectors.toList());

Only filtered1 has the expected two entries, the other lists are empty. Why is KEY::startsWith not working as expected?

Mani76
  • 357
  • 1
  • 3
  • 14

2 Answers2

2

You are doing it the wrong way round.

.filter(KEY::startsWith)

means the same as

.filter(k->KEY.startsWith(k))

This checks whether KEY starts with the element to filter.

but you want to check if the element to filter starts with key.

.filter(k->k.startsWith(KEY))

If you look at the Java Language Specification, chapter 15.13, you can see this:

The target reference of an instance method (§15.12.4.1) may be provided by the method reference expression using an ExpressionName, a Primary, or super, or it may be provided later when the method is invoked. The immediately enclosing instance of a new inner class instance (§15.9.2) is provided by a lexically enclosing instance of this (§8.1.3).

This means that it will use the variable on the left side of the :: operator as the object to call startsWith.

This is also explained in the method references page of the Java Tutorials:

Reference to an Instance Method of an Arbitrary Object of a Particular Type The following is an example of a reference to an instance method of an arbitrary object of a particular type:

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

The equivalent lambda expression for the method reference String::compareToIgnoreCase would have the formal parameter list (String a, String b), where a and b are arbitrary names used to better describe this example. The method reference would invoke the method a.compareToIgnoreCase(b).

dan1st
  • 12,568
  • 8
  • 34
  • 67
1

KEY::startsWith means:

x -> KEY.startsWith(x)

So you are checking if key= starts with key=123 etc, which is the wrong way around. You should be checking whether key=123 starts with key=. The same goes for "key="::startsWith.

You can't really use a method reference to startsWith here. If there is a method called something like isStartOf in String:

public boolean isStartOf(String other) {
    return other.startsWith(this);
}

then you could use KEY::isStartOf, but I don't think there is a method like that built in.

Sweeper
  • 213,210
  • 22
  • 193
  • 313