I have a class
class ColumnTags {
String Name;
Collection<String> columnSemanticTags;
// constructor and getter and setters and other relevant attributes
}
I want to get the columnSemanticTags from a list of ColumnTags for a given name.
The corresponding method is as follows
public Collection<String> getTags(String colName, List<ColumnTags> colList)
{
Collection<String> tags = new ArrayList();
for(ColumnTag col:colList){
if(colName.equals(col.getName())){
tags = col.getColumnSemanticTags();
break;
}
}
return tags;
}
Want to convert the for loop to a java stream . I have tried
tags = colList.stream().filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.collect(Collectors.toCollection());
I am getting compilation error. I am not aware what should be the Supplier . Have tried ArrayList::new . I have also tried casting it to ArrayList , but no success.
Can someone advice me what am I assuming wrong or what should be the expected way to handle this scenario.
With the solution , can someone explain as to why .collect() is a wrong way of tackling this solution.
public Collection<String> getTags(String colName, List<ColumnTags> colList) {
return colList.stream().filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.findFirst().orElse(new ArrayList<>());
}
An easier way of going about this would be to simply filter a Stream to find exactly what you're looking for. If it is found, then return it, otherwise return an empty ArrayList:
return colList.stream()
.filter(c -> colName.equals(c.getName()))
.map(ColumnTag::getColumnSemanticTags)
.findFirst()
.orElseGet(ArrayList::new);
If you really want to use collect, you must call flatMap. That merges all of the lists (which are come from map(col -> col.getColumnSemanticTags())) into a single stream which contains all of the items.
List<String> tags = colList.stream()
.filter(col -> colName.equals(col.getName()))
.map(col -> col.getColumnSemanticTags())
.flatMap(collection -> collection.stream())
.collect(Collectors.toList());
Related
Currently im learning about stream and want to implement a method which accepts a string. The String starts with a certain word and ends with the same. The given example is "breadtunabread". The method return the word in between the bread.
public String getTopping(String s){
Stream<String> stream = Stream.of(s);
stream.filter(t -> t.startsWith("bread") && t.endsWith("bread")).
forEach(t -> Stream.of(t.split("bread")[1]).collect(Collectors.toList()));
}
I'd like to either save it to a List or change it directly so it returns a String.
Is it possible to get the first value from the stream and not use collect?
I somehow made it work using forEach and adding the value to an ArrayList and returning it but i'd like to know whether there is a way to do it directly using the stream.
Thanks in advance.
And to return just a String:
public String getTopping(String s, String toReplace) {
Stream<String> stream = Stream.of(s);
return stream.filter(t -> t.startsWith(toReplace) && t.endsWith(toReplace))
.findFirst()
.map(t -> t.replaceAll(toReplace, ""))
.orElseThrow(RuntimeException::new);
//.orElseThrow(() -> new NoBreadException("s"));
}
Stream<String> stream = Stream.of("breadtunabread");
List<String> stringList =
stream
.filter(t -> t.startsWith("bread") && t.endsWith("bread"))
.map(t -> (t.split("bread")[1]))
.collect(Collectors.toList());
Is this what you are looking for?
What others mentioned are correct that this is completely unnecessary. I posted this as you have mentioned that yuo are learning streams.
Just like #Naman pointed out, you don't need a Stream for this. String#replaceAll will quite literally replace all instances of the String (bread) with empty String values and in the end you get you're topping. Added the base parameter in case you're a monster like me and eat cheese between pieces of ham.
public static String getTopping(String value, String base) {
return value.replaceAll(base, "");
}
String topping = getTopping("breadtunabread", "bread")
Assuming you have a List of items you want to get the toppings of.
List<String> sandwhiches = Arrays.asList(
"breadtunabread",
"breadchickenbread",
"breadcheesebread",
"breadturkeybread",
"breadlambbread"
);
List<String> toppings = sandwhiches.stream()
.map(sandwhich -> getTopping(sandwhich, "bread"))
.collect(Collectors.toList());
Result
[tuna, chicken, cheese, turkey, lamb]
I have this simple code where I use a stream and a .map() function.
I do a null check for the id, and inside it a add a continue
The continue gives me an error: Continue outside of loop
When I remove the continue I don't get an error, but I don't know if the behaviour is the same?
public List<Long> getIds(final Long[][] value){
List<Long> list = Arrays.stream(value).map(result ->{
final Long id = result[1];
if(id == null){
continue; // This part doesn't work (error: Continue outside of loop)
}
return id;
}).collect(Collectors.toList());
}
Any suggestion on why this happens with .streams? Whereas, when I don't use the stream I can use continue.
The question has been marked as duplicate, but it's not the case. Using return surely works in forEach, where no return type is requested, but not in map.
continue works in a for loop. You can use flatMap as a workaround:
List<Long> list = Arrays.stream(value).flatMap(result ->{
final Long id = result[1];
return Stream.ofNullable(id);
}).collect(Collectors.toList());
You can also make it more concise by using Stream.ofNullable directly as #Naman suggests:
List<Long> list = Arrays.stream(value)
.flatMap(result -> Stream.ofNullable(result[1]))
.collect(Collectors.toList());
The other, more elegant version of my firstly proposed method by #Holger would be to use the predicate in the filter:
List<Long> list = Arrays.stream(value)
.map(result -> result[1])
.filter(Objects::nonNull)
.collect(Collectors.toList());
So, I have sorted by condition list of objects
private Observable<CallServiceCode> getUnansweredQuestionList() {
return Observable.fromIterable(getServiceCodeArrayList())
.subscribeOn(Schedulers.computation())
.filter(iServiceCode -> iServiceCode.getServiceCodeFormStatus().isUnanswered());
}
and now what I need to do:
Every object has list servicePartList , I need to filter this list by condition and eventually if final size of this filtered list >0, so I need to add object that contains this list CallServiceCode object as a key and this filtered list as a value.
So it should be like this:
private Map<CallServiceCode, ArrayList<CallServicePart>> getSortedMap() {
Map<CallServiceCode, ArrayList<CallServicePart>> result = new HashMap<>();
getUnansweredQuestionList()
.filter(callServiceCode -> Observable.fromIterable(callServiceCode.getCallServicePartList()) //
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered())//
.isNotEmpty())
.subscribe(callServiceCode -> result.put(callServiceCode, Observable.fromIterable(callServiceCode.getCallServicePartList()) //
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered()));
return result;
}
But there is no such method isNotEmpty() in RxJava2 and also it is not right to add key like this:
Observable.fromIterable(callServiceCode.getCallServicePartList())
.filter(servicePart -> servicePart.getServicePartFormStatus().isUnanswered())
So question is how to make it properly?
One solution could be to use collect to create the Map directly from the observable:
return getUnansweredQuestionList()
.collect(HashMap<CallServiceCode, List<CallServicePart>>::new,(hashMap, callServiceCode) -> {
List<CallServicePart> callServiceParts = Observable.fromIterable(callServiceCode.getServicePartList())
.filter(s -> !s.getServicePartFormStatus().isUnanswered())
.toList().blockingGet();
if (!callServiceParts.isEmpty())
hashMap.put(callServiceCode, callServiceParts);
}).blockingGet();
If you extract filtering into a method (could be also member of CallServiceCode) then the code is much cleaner:
return getUnansweredQuestionList()
.collect(HashMap<CallServiceCode, List<CallServicePart>>::new, (hashMap, callServiceCode) -> {
List<CallServicePart> filteredParts = getFilteredServiceParts(callServiceCode.getServicePartList());
if (!filteredParts .isEmpty())
hashMap.put(callServiceCode, filteredParts);
}).blockingGet();
I have an entity Employee
class Employee{
private String name;
private String addr;
private String sal;
}
Now i have list of these employees. I want to filter out those objects which has name = null and set addr = 'A'. I was able to achieve like below :
List<Employee> list2= list.stream()
.filter(l -> l.getName() != null)
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
Now list2 will have all those employees whose name is not null and then set addr as A for those employees.
What i also want to find is those employees which are filtered( name == null) and save them in DB.One way i achieved is like below :
List<Employee> list2= list.stream()
.filter(l -> filter(l))
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
private static boolean filter(Employee l){
boolean j = l.getName() != null;
if(!j)
// save in db
return j;
}
1) Is this the right way?
2) Can we do this directly in lambda expression instead of writing separate method?
Generally, you should not use side effect in behavioral parameters. See the sections “Stateless behaviors” and “Side-effects” of the package documentation. Also, it’s not recommended to use peek for non-debugging purposes, see “In Java streams is peek really only for debugging?”
There’s not much advantage in trying to squeeze all these different operations into a single Stream pipeline. Consider the clean alternative:
Map<Boolean,List<Employee>> m = list.stream()
.collect(Collectors.partitioningBy(l -> l.getName() != null));
m.get(false).forEach(l -> {
// save in db
});
List<Employee> list2 = m.get(true);
list2.forEach(l -> l.setAddr("A"));
Regarding your second question, a lambda expression allows almost everything, a method does. The differences are on the declaration, i.e. you can’t declare additional type parameters nor annotate the return type. Still, you should avoid writing too much code into a lambda expression, as, of course, you can’t create test cases directly calling that code. But that’s a matter of programming style, not a technical limitation.
If you are okay in using peek for implementing your logic (though it is not recommended unless for learning), you can do the following:
List<Employee> list2= list.stream()
.peek(l -> { // add this peek to do persistence
if(l.getName()==null){
persistInDB(l);
}
}).filter(l -> l.getName() != null)
.peek(l -> l.setAddr("A"))
.collect(Collectors.toList());
You can also do something like this:
List<Employee> list2 = list.stream()
.filter(l->{
boolean condition = l.getName()!=null;
if(condition){
l.setAddr("A");
} else {
persistInDB(l);
}
return condition;
})
.collect(Collectors.toList());
Hope this helps!
I have a case where A has a list of B and B has some property i need.
pseudocode structure
class A
List<B> elements;
class B
Property property;
I have List of A. In order, to get property i should go through the double foreach loop to get the property i want. Like this:
String myFutureProp = null;
for (A a: list of A) {
for(B b: list of B) {
if("MY_PROPERTY".equals(b.getKey) {
myFutureProp = b.getValue();
}
}
}
I was thinking to get it more tasty using Stream API. I was looking forward with
forEach() solution:
final String[] myFutureProp = {null}
a.getElements()
.foreach(b -> b.getElements().stream()
.filter("MY_PROPERTY"::equals)
.forEach(prop -> myFutureProp[0] = (String)prop.getValue);
Then i'm taking myFutureProp[0] but it looks ugly to be honest. Is it any another solution in Stream API that i'm able to use?
I hope I got your class structure right.
You can use flatMap to get a Stream of all the Propertys of all the B instances, and return any property having the required key (if found) :
Optional<String> myFutureProp =
aList.stream()
.flatMap(a->a.getElements().stream().map(B::getProperty))
.filter(prop->prop.getKey().equals("MY_PROPERTY"))
.map(Property::getValue)
.findAny();
This should do a work -> get last matching value
final String value = bList.stream()
.flatMap(bElement -> bElement.elements().stream())
.map(aElement -> aElement.getProperty())
.filter(property -> property.getKey().equals("MY_PROPERTY"))
.map(property -> property.getValue())
.reduce((a,b) -> b).orElse(null);