(Predicate<? super String> s) or (String s) - java

I have a TreeSet of Strings (hardcoded).
Want to check that a given parameter String eg. "Person" if present in the TreeSet then return true otherwise return false.
Here I am confused by the Eclipse message regarding
(Predicate<? super String> s) vs (String s):
The method anyMatch(Predicate) in the type Stream is not applicable for the arguments (String)
Please guide.
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
public class SystemLabelValidator {
public static boolean ValidateSystemLabel( String s) {
String j = s;
boolean b = false;
Set <String> SystemLabels = new TreeSet<String>();
// Unique Strings
SystemLabels.add("Person");
SystemLabels.add("Player");
SystemLabels.add("Hospital");
SystemLabels.add("Nurse");
SystemLabels.add("Room");
System.out.println("\n==> Loop here.");
for (String temp : SystemLabels) {
System.out.println(temp);
if(SystemLabels.stream().anyMatch(j)) {
System.out.println("I need to return Boolean");
}
return b;
}
return b;
}
}

There is no need to use a Predicate here. In order to check if the String is present in your TreeSet just use :
return systemLabels.contains("Person");
If you still insist on using anyMatch then you can do :
public static boolean validateSystemLabel(String s) {
return systemLabels.stream().anyMatch(i -> i.equals(s));
}
Remember, a predicate expression needs to evaluate to a boolean value but in the code, you are passing in a String hence the compilation error.

The problem in your solution is this line:
SystemLabels.stream().anyMatch(j);
Basically anyMatch() expects Predicate as input not String.
But your problem has simpler solution:
import java.util.Set;
import java.util.TreeSet;
public class SystemLabelValidator {
private static final Set<String> SYSTEM_LABLES = new TreeSet<>(Arrays.asList("Person", "Player", "Hospital", "Nurse", "Room"));
public static boolean validateSystemLabel( String value) {
return SYSTEM_LABLES.contains(value);
}
}

The signature of anyMatch is
boolean anyMatch(Predicate<? super T> predicate)
In your case, the argument must be a Predicate<? super String>. That is, a method which can take a string and return a boolean. This method is looking for one of the following
Predicate<String> (e.g. String::isEmpty)
Predicate<Object> (e.g. Objects::isNull)
Predicate<CharSequence>
Predicate<Comparable<String>>
Predicate<Serializable>
You have attempted to give it a string, which does not match the signature. One way to fix this would be:
if(SystemLabels.stream().anyMatch(j::equals)) {
System.out.println("I need to return Boolean");
}

Related

How to replace "!= null" statement with method reference?

My Intelij shows me that I can replace the lambda .filter(bean -> bean != null) with a method reference. Its is obviuos for me how to do it when I have a Class or an Object with methods. But how to do a reference on != null, is there a Class that has some kind of isNull() method?
This is the exact reason why the Objects::nonNull API exists:
.filter(Objects::nonNull);
Moved from comment to answer
Of course, as Aomine says, you can use Objects::nonNull for this.
But for code that doesn't have an available function, I think it's useful to know that you can use a lot of things in lambda expressions; You can use code blocks or just write a simple method following the contract of the lambda and passing a reference to that method.
class Test {
public static void main(String[] args){
Stream<Object> stream = ...;
// lambda from code block
stream.filter(bean -> {return bean != null;});
Predicate<Object> p1 = o -> {return o != null;};
// which resolves to the below
Predicate<Object> p2 = new Predicate<>() {
#Override public boolean test(Object o) {
return o != null;
}
};
// or pass your own method that can resolve to the correct functional class
stream.filter(Test::myPredicateTest);
}
// meets the contract of a Predicate<Object>.test(Object o) returning boolean
public static boolean myPredicateTest(Object o) {
return o != null;
}
}
Yes, of course; you can use the Objects::nonNull API.
package com.logicbig.example.objects;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class NonNullExample {
public static void main(String... args) {
List<String> list = Arrays.asList(null, null, "first_element", "second_element", null);
list = list.stream().filter(Objects::nonNull).collect(Collectors.toList());
System.out.println(list);
}
}

Group by object property in java flux

Given the following data structure Data and Flux<Data> what is idiomatic way to achieve grouping into series of lists based on some property:
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;
class Scratch {
private static class Data {
private Integer key;
private String value;
public Data(Integer key, String value) {
this.key = key;
this.value = value;
}
public Integer getKey() {
return key;
}
public String getValue() {
return value;
}
public static Data of(Integer key, String value) {
return new Data(key, value);
}
#Override
public String toString() {
return value;
}
}
public static void main(String[] args) {
Flux<Data> test = Flux.just(
Data.of(1, "Hello"),
Data.of(1, "world"),
Data.of(2, "How"),
Data.of(2, "are"),
Data.of(2, "you"),
Data.of(3, "Bye"));
test.bufferUntil(new Predicate<Data>() {
Integer prev = null;
#Override
public boolean test(Data next) {
boolean collect = prev != null && !Objects.equals(prev, next.getKey());
prev = next.getKey();
return collect;
}
}, true).subscribe(e -> System.out.println(e.toString()));
}
}
Output:
[Hello, world]
[How, are, you]
[Bye]
I am aware of groupBy function on Flux, but this gives me again a Flux, not a list. Current solution I have described above works, but it does not feel 100% idiomatic because I had to use anonymous class instead of lambda. I could have use lambda and AtomicReference outside from lambda, but that too does not feel 100% right. Any suggestions?
You can also use collectMultimap which allows you to have Map<K, Collection<T>. In this case collectMultimap will return: Mono<Map<Integer,Collection<Data>>>:
test.collectMultimap( Data::getKey )
.subscribe( dataByKey -> System.out.println( dataByKey.toString() ) );
Output:
{1=[Hello, world], 2=[How, are, you], 3=[Bye]}
Here is a solution using groupBy operator. I have grouped the data by the common key. The groupBy operator gives me a Flux of GroupedFlux. GroupedFlux is a subclass of Flux, so I apply flatMap and convert an individual groupedFlux to a List<Data> using the collectList operator. Like this, I get a Flux<List<Data>>, which I then subscribe to and print, as asked by you.
test.groupBy(Data::getKey)
.flatMap(Flux::collectList)
.subscribe(listOfStringsHavingDataWithSameKey -> System.out.println(listOfStringsHavingDataWithSameKey.toString()));
Do checkout the documentations for Flux and GroupedFlux.

method returning either a collection or a single value

I have a class with various properties and I would like to write a wrapper method around them in order to loop around them more easily.
Some properties return a collection of values, some a single value. And I'm looking for the best approach for this.
My first approach is to let the wrapper method return whatever the property getters return.
public class Test {
public Object getValue(String propName) {
if ("attr1".equals(propName)) return getAttribute1();
else if ("attr2".equals(propName)) return getAttribute2();
else return null;
}
public List<String> getAttribute1() {
return Arrays.asList("Hello","World");
}
public String getAttribute2() {
return "Goodbye";
}
public static void main(String[] args) {
final Test test=new Test();
Stream.of("attr1","attr2")
.forEach(p-> {
Object o=test.getValue(p);
if (o instanceof Collection) {
((Collection) o).forEach(v->System.out.println(v));
}
else {
System.out.println(o);
}
});
}
}
The bad point with this approach is that the caller has to test himself whether the result is a collection or not.
Other approach, seamless for the caller, is to always return a collection, ie. the wrapper function wraps the single values into a Collection. Here an HashSet, but we can imagine an adhoc, minimum 1 element list.
public class TestAlt {
public Collection getValue(String propName) {
if ("attr1".equals(propName))
return getAttribute1();
else if ("attr2".equals(propName)) {
Set s = new HashSet();
s.add(getAttribute2());
return s;
}
else
return null;
}
public List<String> getAttribute1() {
return Arrays.asList("Hello", "World");
}
public String getAttribute2() {
return "Goodbye";
}
public static void main(String[] args) {
final TestAlt test = new TestAlt();
Stream.of("attr1", "attr2")
.forEach(p -> {
test.getValue(p).forEach(v -> System.out.println(v));
});
}
Performance-wise, design-wise, ... what's your opinion on these approaches ? Do you have better ideas ?
Well, you could pass the action to be performed on each attribute to the object and let the object decide on how to handle it. E.g.:
in Class Test:
public void forEachAttribute(String propName, Handler h) {
if ("attr1".equals(propName))
h.handle(getAttribute1());
else if ("attr2".equals(propName)) {
getAttribute2().forEach(o -> h.handle(o))
}
}
And a class Handler with the function handle(String s), that does, what you want to do.
If you cannot edit Test, you can also move the function outside Test
public void forEachTestAttribute(Test t, String propName, Handler h)...
Performance-wise: This removes an if-clause
Design-wise: This removes a cast, but creates more classes.
*Edit: It also maintains type-security, and if there are multiple kinds of attributes (String, int, etc.) you could add more handle-functions, to still maintain type-security.
Regarding the design I would rewrite your code into this:
TestAlt.java
import java.util.*;
import java.util.stream.Stream;
public class TestAlt {
private Map<String, AttributeProcessor> map = AttributeMapFactory.createMap();
public Collection getValue(String propName) {
return Optional
.ofNullable(map.get(propName))
.map(AttributeProcessor::getAttribute)
.orElse(Arrays.asList("default")); //to avoid unexpected NPE's
}
public static void main(String[] args) {
final TestAlt test = new TestAlt();
Stream.of("attr1", "attr2")
.forEach(p -> test.getValue(p).forEach(v -> System.out.println(v)));
}
}
AttributeMapFactory.java
import java.util.HashMap;
import java.util.Map;
public class AttributeMapFactory {
public static Map<String, AttributeProcessor> createMap() {
Map<String, AttributeProcessor> map = new HashMap<>();
map.put("attr1", new HiAttributeProcessor());
map.put("attr2", new ByeAttributeProcessor());
return map;
}
}
AttributeProcessor.java
import java.util.Collection;
public interface AttributeProcessor {
Collection<String> getAttribute();
}
HiAttributeProcessor.java
import java.util.Arrays;
import java.util.Collection;
public class HiAttributeProcessor implements AttributeProcessor{
#Override
public Collection<String> getAttribute() {
return Arrays.asList("Hello", "World");
}
}
ByeAttributeProcessor.java
import java.util.Arrays;
import java.util.Collection;
public class ByeAttributeProcessor implements AttributeProcessor{
#Override
public Collection<String> getAttribute() {
return Arrays.asList("Goodbye");
}
}
The main point is that you get rid of if-else statements using map and dynamic dispatch.
The main advantage of this approach is that your code becomes more flexible to further changes. In case of this small programm it does not really matter and is an overkill. But if we are talking about large enterprise application, then yes, it becomes crucial.

Match List of objects against list of properties

I'm trying to use hamcrest matchers to match a list of objects against a list/array of their properties. For one property value this is not a problem, because I can do something like this:
assertThat(savedGroup.getMembers(),
containsInAnyOrder(hasProperty("name", is(NAMES[0]))));
For multiple property values I can use multiple hasProperty() calls
assertThat(savedGroup.getMembers(),
containsInAnyOrder(
hasProperty("name", is(NAMES[0])),
hasProperty("name", is(NAMES[1]))));
But is there a generic way to match against all values in the NAMES array?
The best way (IMO) to do this would be to combine the overloaded containsInAnyOrder Matcher along with a custom FeatureMatcher. Ultimately your code would look like this:
String[] expectedNames = new String[] { "John", "Bob", "Carol"};
assertThat(savedGroup.getMembers(), hasNames(expectedNames));
hasNames is implemented as follows:
private Matcher<Iterable<? extends Member>> hasNames(String[] expectedNames) {
return containsInAnyOrder(Arrays.stream(expectedNames).map(name -> name(name)).collect(Collectors.toList()));
}
And the final part is the call to name which generates a Matcher that will extract a property in a type-safe way from your object:
private Matcher<Member> name(String name) {
return new FeatureMatcher<Member, String>(equalTo(name), "name", "name") {
#Override
protected String featureValueOf(Member actual) {
return actual.getName();
}
};
}
The benefit of doing it this is way is that:
You get the benefit of type-safety instead of using hasProperty
Your test now describes what you actual want to match on, i.e. hasNames
The code produced is now more flexible and composable. Want to match a single objects name? All you now need to do is assertThat(member, has(name("Fred")))
You can get even more composability by moving the equalTo sub-matcher to be part of the hasNames call like this:
private Matcher<Iterable<? extends Member>> hasNames(String[] expectedNames) {
return containsInAnyOrder(Arrays.stream(expectedNames).map(name -> name(equalTo(name))).collect(Collectors.toList()));
}
private Matcher<Member> name(Matcher<String> nameMatcher) {
return new FeatureMatcher<Member, String>(nameMatcher, "name", "name") {
#Override
protected String featureValueOf(Member actual) {
return actual.getName();
}
};
}
One of containsInAnyOrder's overloads accepts a collection of matchers as its argument. Thus you could do something like this:
assertThat(
savedGroup.getMembers(),
containsInAnyOrder(
Stream.of(NAMES)
.map(name -> hasProperty("name", is(name)))
.collect(Collectors.toList())
));
(if using Java 8, otherwise would need to add a loop building up the collection)
Need to make some cleanup (description output), but I think it does solve your problem:
package org.example.matchers;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.TypeSafeMatcher;
public class ContainsArrayElementsInAnyOrder<T> extends TypeSafeMatcher<List<T>> {
private T[] toMatch;
public ContainsArrayElementsInAnyOrder(final T[] toMatch) {
this.toMatch = toMatch;
}
#Override
protected boolean matchesSafely(List<T> item) {
if(item.size() != toMatch.length) {
return false;
}
for (T t : toMatch) {
if(!item.contains(t)) {
return false;
}
}
return true;
}
#Override
public void describeMismatchSafely(List<T> item, Description mismatchDescription) {
mismatchDescription.appendValueList("[", ",", "]", item);
}
#Override
public void describeTo(Description description) {
description.appendValueList("[", ",", "]", toMatch);
}
#Factory
public static <T> ContainsArrayElementsInAnyOrder<T> containsArrayElementsInAnyOrder(T[] elements) {
return new ContainsArrayElementsInAnyOrder<T>(elements);
}
}
Test:
#Test
public void shouldContainsInAnyOrderSameElementsInArrayAsInList() {
final String[] NAME = new String[]{"name3", "name1", "name2"};
final List<String> result = new ArrayList<>(3);
result.add("name2");
result.add("name1");
result.add("name4");
assertThat(result, containsArrayElementsInAnyOrder(NAME));
}
Output if not match:
java.lang.AssertionError:
Expected: ["name3","name1","name2"]
but: ["name2","name1","name4"]
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at org.junit.Assert.assertThat(Assert.java:923)
at ..

Find disjoint set irrespective of case of the strings

I am using Collection.disjoint to find the disjoint set of two string collections c1, c2. But it does not ignore cases, for example - string str is different than Str.
return Collections.disjoint(c1, c2);
Can I find the disjoint of both collections ignoring their cases without using a for loop?
If you absolutely insist that no for loop is used, you can always find the disjoint between two Collections of lowercased Strings. Using Google Guava, it should be something like:
package ru.zombator.stackoverflow;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public final class DisjointIgnoreCase {
public static void main(String[] args) {
Collection<String> coll1 = Arrays.asList("donald", "Duck");
Collection<String> coll2 = Arrays.asList("DONALd", "Donut");
Collection<String> coll3 = Arrays.asList("Homer", "DONUT");
Collection<String> coll4 = Arrays.asList("DONALD", "duck");
// will all print false
System.out.println(disjointIgnoreCase(coll1, coll2));
System.out.println(disjointIgnoreCase(coll2, coll3));
System.out.println(disjointIgnoreCase(coll1, coll4));
// will print true (no common elements)
System.out.println(disjointIgnoreCase(coll1, coll3));
}
private static boolean disjointIgnoreCase(Collection<String> coll1, Collection<String> coll2) {
return Collections.disjoint(lowercased(coll1), lowercased(coll2));
}
private static Collection<String> lowercased(Collection<String> coll) {
return Collections2.transform(coll, new Function<String, String>() {
#Override
public String apply(String input) {
return input.toLowerCase(Locale.US);
}
});
}
}
An easier way is to convert the values of both lists to lowercase. Creating new lists is not needed as the function is applied to the values the value references are the same - inplace change.
c1.stream().map(s -> s.toLowerCase()).collect(Collectors.toList());
c2.stream().map(s -> s.toLowerCase()).collect(Collectors.toList());
Collections.disjoint(c1, c2);
One cannot put directly the mapping in the disjoint because multiple levels of processing is not allowed here.

Categories