Using method reference to remove elements from a List - java

What's wrong with my code?
I want to remove all the elements starting with A from the List list:
public static void main(String[] args) {
Predicate<String> TTT = "A"::startsWith;
List<String> list = new ArrayList<>();
list.add("Magician");
list.add("Assistant");
System.out.println(list); // [Magician, Assistant]
list.removeIf(TTT);
System.out.println(list); // expected output: [Magician]
}
However, removeIf doesn't remove anything from the list.

"A"::startsWith is a method reference that can be assigned to a Predicate<String>, and when that Predicate<String> is tested against some other String, it would check whether the String "A" starts with that other String, not the other way around.
list.removeIf(TTT) won't remove anything from list, since "A" doesn't start with neither "Magician" nor "Assistant".
You can use a lambda expression instead:
Predicate<String> TTT = s -> s.startsWith("A");
The only way your original "A"::startsWith predicate would remove anything from the list is if the list would contain the String "A" or an empty String.

BiPredicate<String, String> b1 = String::startsWith;
BiPredicate<String, String> b2 = (string, prefix) -> string.startsWith(prefix);
System.out.println(b1.test("chicken", "chick"));
System.out.println(b2.test("chicken", "chick"));
The method reference combines two techniques. **startsWith()**
is an instance method. This means that the first parameter in the lambda is used
as the instance on which to call the method. The second parameter is passed to the
startsWith() method itself. This is example of how method references save a
good bit of typing.

Related

How to add values to Supplier<List>

I feel I have some wrong interpretation of Supplier use-case.
Here is an example where I want to add value to a List. But when I try to fetch the list from supplier, it shows as empty.
Supplier<List<String>> str = ArrayList::new;
str.get().add("Hi");
System.out.println(str.get().size());
Returns: 0
Supplier<List<String>> is a function that gets invoked whenever the get method is run.
In your case, str is similar to the following lambda expression:
Supplier<List<String>> str = () -> new ArrayList<>();
Which means that every time str.get() is called, the function gets called, and the body new ArrayList<>() is executed, thus resulting in a new list every time.
If you want the Supplier to always return the same list, then you need to capture it:
List<String> list = new ArrayList<>();
Supplier<List<String>> str = () -> list;
This way, every time str.get() runs, it will just return the same list it captured. But, IMO, this doesn't seem like good practice, it would seem rather correct to just keep a reference to the variable instead of keeping it behind a Supplier (which implies a function producing a value).
Each call to str.get() will instantiate a new List, which will be empty.
Your two calls to str.get() return two different instances. You added an element to the first List, but the second List is empty.
You have to store a reference to the created List if you want to have access to it:
List<String> list = str.get();
list.add("Hi");
System.out.println(list.size());

Stream sorted method with comparator

I don't understand why code1 works but code2 doesn't compile. Please explain.
//Code1:
Stream<String> s = Stream.of("AA", "BB");
s.sorted(Comparator.reverseOrder())
.forEach(System.out::print);
//Code2:
Stream<String> s = Stream.of("AA", "BB");
s.sorted(Comparator::reverseOrder)
.forEach(System.out::print);
The difference between the two is code1 uses Comparator.reverseOrder() while code2 uses Comparator::reverseOrder
Because the first example is a factory-method so when you inspect it, you see that you get a comparator back.
But the second one is a method-reference which you could write like this:
Stream<String> s = Stream.of("AA", "BB");
s.sorted(() -> Comparator.reverseOrder()) // no semantic difference!
.forEach(System.out::print);
But it has a whole different meaning because this time you are given Stream#sorted() a Supplier<Comparator<?>> but it just needs a Comparator<?>
Small Sidenote: Don't store streams in variables, use them directly. So i would suggest you just write:
Stream.of("AA", "BB")
.sorted(Comparator.reverseOrder())
.forEach(System.out::print);
The error message from the compiler should tell you that.
sorted() expects a Comparator instance. Comparator.reverseOrder()returns a Comparator instance. So that works fine.
Comparator::reverseOrder is a method reference to the reverseOrder() method of Comparator. So your code basically says: each time you need to compare two strings, pass them as argument to Comparator.reverseOrder to compare them. But that can't possibly work. This method takes nothing as argument, and returns a Comparator. So it doesn't match the signature of a Comparator<String>, which is supposed to take two Strings as argument, and return an integer.
If you had a method such as
class Foo
public static int compareStrings(String s1, String s2) {
...
}
}
Then you could use
sorted((s1, s2) -> Foo.compareStrings(s1, s2))
which you can transform, using a method reference, to
sorted(Foo::compareStrings)
Because compareStrings, just like the unique abstract method of Comparator<String>, takes two Strings as argument and returns an int.

Java stream toArray() convert to a specific type of array

Maybe this is very simple but I'm actually a noob on Java 8 features and don't know how to accomplish this. I have this simple line that contains the following text:
"Key, Name"
and I want to convert that line into a String array, separating each value by the comma (,), however, I also want to trim every field before returning the final array, so I did the following:
Arrays.stream(line.split(",")).map(String::trim).toArray();
However, this returns an Object[] array rather than a String[] array. Upon further inspection, I can confirm that the contents are actually String instances, but the array itself is of Object elements. Let me illustrate this, this is what the debugger says of the returned object:
Object[]:
0 = (String) "Key"
1 = (String) "Name"
As far as I can tell, the problem is in the return type of the map call, but how can I make it return a String[] array?
Use toArray(size -> new String[size]) or toArray(String[]::new).
String[] strings = Arrays.stream(line.split(",")).map(String::trim).toArray(String[]::new);
This is actually a lambda expression for
.toArray(new IntFunction<String[]>() {
#Override
public String[] apply(int size) {
return new String[size];
}
});
Where you are telling convert the array to a String array of same size.
From the docs
The generator function takes an integer, which is the size of the desired array, and produces an array of the desired size. This can be concisely expressed with an array constructor reference:
Person[] men = people.stream()
.filter(p -> p.getGender() == MALE)
.toArray(Person[]::new);
Type Parameters:
A - the element type of the resulting array
Parameters:
generator - a function which produces a new array of the desired type and the provided length
String[]::new is a function that invokes the new "pseudo-method" for the String[] type just like String::trim is a function that invokes the real trim method of the String type. The value passed to the String::new function by toArray is the size of the collection on the right-hand side of the .toArray() method invocation.
If you replaced String[]::new with n->new String[n] you might be more comfortable with the syntax just like you could replace String::trim with the less cool s->s.trim()

How to use hamcrest contains to compare 2 lists?

Why does this test fail? I know contains works when you pass in individual strings separated by commas but I wanted to see if it's possible to just pass in an entire list of strings instead. I just want to make sure that list 1 contains all of the contents of list 2.
#Test
public void testContains() {
String expected1 = "hello";
String expected2 = "goodbye";
List<String> expectedStrings = new ArrayList<>();
expectedStrings.add(expected1);
expectedStrings.add(expected2);
List<String> actualStrings = new ArrayList<>();
actualStrings.add(expected1);
actualStrings.add(expected2);
assertThat(actualStrings, contains(expectedStrings));
}
Is it considered acceptable to use this assertion instead?
assertThat(actualStrings, is(expectedStrings));
There is no overloaded contains method which takes a list of expected values.
In the statement assertThat(actualStrings, contains(expectedStrings))
the following method (in the Matchers class) is called:
<E> org.hamcrest.Matcher<java.lang.Iterable<? extends E>> contains(E... items)
Basically you are saying that you expect a list with one element and this element is expectedStrings but in fact it is expected1 (E is of type List<String> and not String). To verify add the following to the test which should then pass:
List<List<String>> listOfactualStrings = new ArrayList<>();
listOfactualStrings.add(actualStrings);
assertThat(listOfactualStrings, contains(expectedStrings));
To make the assertion work you have to convert the list to an array:
assertThat(actualStrings, contains(expectedStrings.toArray()));
If you want to apply a matcher for each item in a list you can use the everyItem matcher, like so:
everyItem(not(isEmptyOrNullString()))

Comparing a string in a string object using java

I'm having a config entry, from which I'm loading into an String array like
String s = "abc$#def$#ghi";
String[] scbHLNewArray = s.split("\\$\\#");
Here I'm comparing a string with the array values after splitting it like ,
for(String arrNewErrorInfo : scbHLNewArray) {
LOG.info("SCB HL New Error Value :"+arrNewErrorInfo+"\n");
if(errorInfo.equals(arrNewErrorInfo)) {
LOG.info("SCB HL Matched New value is :"+arrNewErrorInfo);
newState = ApplicationState.NEW;
addApplicationEvent(application.getId(),comment, ApplicationEventType.COMMENT,BBConstants.AUTOBOT);
scbHLNewStatus = "Matched";
break;
}
}
I want to use some util classes like List.. Any idea on append to list and compare the string with the list objecT?
Thanks,
Nizam
you can do this with List contains method.
ArrayList<Integer> arrlist = new ArrayList<Integer<(8);
// use add() method to add elements in the list
arrlist.add(20);
arrlist.add(25);
arrlist.add(10);
arrlist.add(15);
// list contains element 10
boolean retval = arrlist.contains(10); // It will return true.
Ok, let's try... First of all, you can create a List Object, wrapping your array very easily:
List<String> myList = Arrays.asList( scbHLNewArray );
Be carefull, because you can NOT add to this list, as it only wraps your array. If you want a list you can add to, you would have to create a new one, for example:
List<String> myModifiableList = new ArrayList<String>( myList );
This will create a new List that contains all the Strings from the first one but is also modifiable (you can add Strings, if you want).
In any case, you can use "contains", as Pratik has already shown, to test if a String is inside your list:
if (myList.contains("someString")) { ... }
This works because the String class already has well implemented equals(...) and hashCode() methods. If you want to put other Object than Strings into your list, you would have to make sure that these methods are implemented well, otherwise contains might not work as expected.
Yes you can use a list of course, you need to :
1. Take the result of split as an array.
2. Then convert this array to a list.
String s = "abc$#def$#ghi";
String[] scbHLNewArray = s.split("\\$\\#");
List<String> list=Arrays.asList(scbHLNewArray); //convert the array to a list
Take a look at Arrays.asList(Array a) and this Tutorial for further information about it.
And then to search the wanted String object you can use indexOf(Object o) or contains(Object o) List methods

Categories