How to change the below code to remove if-else and use Java8
List<String> list1;
List<String> list2;
List<String> list3;
String str;
if(list1.contains(str)){
event.getMessage().setInvocationProperty("ABC","ABC1");
}
else if(list2.contains(str)){
event.getMessage().setInvocationProperty("ABC2","ABC3");
}
else if(list3.contains(str)){
event.getMessage().setInvocationProperty("ABC4","ABC5");
}
Here is how to do it by creating what I'll call a HoldingObject, but you can name it with something more close to your business.
I'm using Lombok's #Value annotation and also java-9's List#of factory method
#Value
public static class HoldingObject {
List<String> list;
String invocationProperty1;
String invocationProperty2;
public void setInvocationPropertyFor(Event event) {
event.getMessage().setInvocationProperty(invocationProperty1, invocationProperty2);
}
}
Note that doing event.getMessage() repeatedly might not be thread-safe if event is accessed through multiple threads
HoldingObject firstObject = new HoldingObject(list1, ABC, ABC1);
HoldingObject secondObject = new HoldingObject(list1, ABC2, ABC3);
HoldingObject thirdObject = new HoldingObject(list1, ABC4, ABC5);
List.of(firstObject, secondObject, thirdObject)
.stream()
.filter(object -> object.getList().contains(str))
.findFirst()
.ifPresent(h -> h.setInvocationPropertyFor(event));
It is possible to do it without if-else, but for this case, if-else would still be better than using streams.
List<String> list1;
List<String> list2;
List<String> list3;
String str;
Map<List<String>, List<Param>> paramMap = new HashMap<>();
paramMap.put(list1,List.of(ABC,ABC1));
paramMap.put(list2,List.of(ABC2,ABC3));
paramMap.put(list3,List.of(ABC4,ABC5));
List.of(list1,list2,list3)
.stream()
.filter(list -> list.contains(str))
.findFirst()
.ifPresent(list -> event.getMessage().setInvocationProperty(paramMap.get(list).get(0),paramMap.get(list).get(1)));
Another solution without using the list as key in paramMap:
Map<Integer, List<Param>> paramMap = new HashMap<>();
paramMap.put(1,List.of(ABC,ABC1));
paramMap.put(2,List.of(ABC2,ABC3));
paramMap.put(3,List.of(ABC4,ABC5));
List<List<String>> lists = List.of(list1,list2,list3);
List<String> mList = lists.stream()
.filter(list -> list.contains(str))
.findFirst()
.ifPresent(list -> {
Integer index = Integer.valueOf(lists.indexOf(list));
event.getMessage()
.setInvocationProperty(paramMap.get(index).get(0),paramMap.get(index).get(1))
});
Related
I'm doing the below two operations
Iterating through a list of Objects and creating a map of String, Boolean based on a condition.
Map<String,Boolean> myMap = new HashMap<>();
Iterator<Person> iterator = personList.iterator();
while (iterator.hasNext()) {
Person person = iterator.next();
if (isValidperson(person)) {
if (person.getName() != null) {
myMap.put(person.getName(), true);
} else {
myMap.put(person.getName(), false);
}
}
}
Now Im checking a list of Names against that map that I created above and if the value is true then adding to a final list
List<String> refinedList = new ArrayList<>();
for (String name : nameList) {
if (myMap.get(name) != null && myMap.get(name)) {
refinedList.add(name);
}
}
I need to simplify the logic using Java streams. The above works fine otherwise.
In the first operation you are filtering out all the non-valid persons, and collecting the valid persons to a map, so:
Map<String,Boolean> myMap = personList.stream()
.filter(YourClass::isValidPerson)
.collect(Collectors.toMap(x -> x.getName(), x -> x.getName() != null))
But really though, the map is going to have at most one false entry, since you can't add multiple nulls into a HashMap, so there isn't much point in using a HashMap at all.
I suggest using a HashSet:
Set<String> mySet = personList.stream()
.filter(YourClass::isValidPerson)
.map(Person::getName)
.filter(Objects::nonNull)
.collect(Collectors.toSet())
And then you can easily check contains with O(1) time:
List<String> refinedList = nameList.stream().filter(mySet::contains).collect(Collectors.toList());
You can directly filter the list by checking contains in nameList and collect the names in list
List<String> refinedList =
personList.stream()
.filter(e -> isValidperson(e))
.map(e -> e.getName())
.filter(Objects::nonNull)
.distinct()
.filter(e -> nameList.contains(e))
.collect(Collectors.toList());
And it better to create a set from nameList to make the contains() operation faster in O(1)
Set<String> nameSet = new HashSet<String>(nameList);
Note: This will works if nameList doesn't contains duplicate.
This should work.
First, create a list of People.
List<Person> personList = List.of(new Person("Joe"),
new Person(null), new Person("Barb"), new Person("Anne"), new Person("Gary"));
Then the nameList. Note it is best to put this in a set to
avoid duplicates, and
make the lookup process more efficient.
Set<String> nameSet = Set.of("Joe", "Anne", "Ralph");
Now this works by
filtering on a valid vs invalid person.
mapping those people to a name
filtering on whether null and then if the name is in the set of names
and placing in a list.
Note: In some cases, lambdas could be replaced by Method References depending on method types and calling contexts.
List<String> names = personList.stream()
.filter(person -> isValidperson(person))
.map(Person::getName)
.filter(name -> name != null && nameSet.contains(name))
.collect(Collectors.toList());
System.out.println(names);
Prints
[Joe, Anne]
Dummy method since criteria not provided
public static boolean isValidperson(Person person) {
return true;
}
Simple person class
class Person {
String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
private List<BusinessObject> createList(int property1, List<String> filenames) {
List<BusinessObject> objectList = new ArrayList();
filenames.forEach(filename -> {
BusinessObject businessObj = parseObject(filename);
if (businessObj.getProperty1() == property1) {
objectList.add(businessObj);
}
});
return objectList;
}
I feel like there must be a more idiomatic or Java 8-ish solution out there, such as
filesnames.forEach(fileName -> parseObject(fileName)).(some method that takes the output of parseObject)
The exact stream equivalent would be:
return filenames.stream()
.map(filename -> parseObject(filename))
.filter(o -> o.getProperty1() == property1)
.collect(Collectors.toCollection(ArrayList::new));
what I'm trying to do
Set<A> set=new HashSet();
Map<String,List<String>> map=new HashMap();
if(map.keySet().contains("key"){
for(String str: map.get("key")
{
for(A a : listOfA)
{
if(a.getString().equalsIgnoreCase(str);
set.add(a);
}
}
}
What I tried
if(map.keySet().contains("key")
{
listOfA
.stream()
.filter(t->t.getString().equalsIgnoreCase(map.get("key")
.stream
.flatMap(c->c.toString()))
.distinct()
.collect(Collectors.toSet()):
}
//error The method equalsIgnoreCase(String) in the type String is not
applicable for the arguments (Stream)
if(map.keySet().contains("key")
{
map.get("key").stream().filter(t->t.equals(listOfA.stream().map(a->a.getString()))).collect(Collectors.toSet());
}
and this method returns Set<String>,obviously as output but I want Set<A> as output
so how to solve this, using functional programming
You can check if any String of map.get("key") is equalsIgnoreCase to getString() of a given A instance by streaming map.get("key") and using anyMatch :
List<String> value = map.get("key");
Set<A> set = null;
if (value != null) {
set = listOfA.stream()
.filter(a->value.stream()
.anyMatch(s->a.getString().equalsIgnoreCase(s)))
.collect(Collectors.toSet());
}
The following solution might scale better if your listOfA is rather large:
List<String> value = map.getOrDefault("key", Collections.emptyList());
Collection<String> c;
if(value.isEmpty()) c=value;
else { c=new TreeSet<>(String.CASE_INSENSITIVE_ORDER); c.addAll(value); }
Set<A> set = listOfA.stream()
.filter(a->c.contains(a.getString()))
.collect(Collectors.toSet());
How to process a list of string and collec it into Map or Immutable map only for those whose value is present
String anotherParam = "xyz";
Map.Builder<String,String> resultMap = ImmutableMap.builder(..)
listOfItems.stream()
.filter(Objects::nonNull)
.distinct()
.forEach(
item -> {
final Optional<String> result =
getProcessedItem(item,anotherParam);
if (result.isPresent()) {
resultMap.put(item, result.get());
}
});
return resultMap.build();
Please tell, is there a better way to achieve this via collect?
If you have access to Apache Commons library you can make use of Pair.class
Map<String, String> resultMap = ImmutableMap.copyof(listOfItems()
.stream()
.filter(Objects::nonNull)
.distinct()
.map(it -> Pair.of(it, getProcessedItem(it,anotherParam))
.filter(pair -> pair.getValue().isPresent())
.collect(toMap(Pair::getKey, pair -> pair.getValue().get())))
But it's a good practice to make special data classes which describes your mapping item->result more specificly
Here is an example, create class like this:
static class ItemResult(){
public final String item;
public final Optional<String> result;
public ItemResult(String item, Optional<String> result){
this.item = item;
this.result = result;
}
public boolean isPresent(){
return this.result.isPresent();
}
public String getResult(){
return result.get();
}
}
And use it like that:
Map<String, String> resultMap = ImmutableMap.copyOf(listOfItems()
.stream()
.filter(Objects::nonNull)
.distinct()
.map(it -> new ItemResult(it, getProcessedItem(it,anotherParam))
.filter(ItemResult::isPresent)
.collect(toMap(ItemResult::item, ItemResult::getResult)))
You can read here why Google gave up the idea of tuples and pairs and don't use them in most cases
If after all you don't want to use any other class you can leverage api of the Optional:
Map.Builder<String,String> resultMap = ImmutableMap.builder(..)
listOfItems.stream()
.filter(Objects::nonNull)
.distinct()
.forEach(item -> getProcessedItem(item,anotherParam)
.ifPresent(result -> resultMap.put(item result));
return resultMap.build();
I have an example here which basically returns list based on simple logic
Given an input list and a list of grouping objects, which has a list field, the method should return a list that contains either all the members of grouping.list if the grouping.name matches any of the strings in the input list OR simply add the input string to the returning list.
After I writing this code, I am thinking it could be made simpler in Java 7 and a better example to use Java 8 Streaming API.
public class CollectorExample {
public static void main(String[] args){
List<String> input = new ArrayList<>();
input.add("foo");
input.add("bar");
input.add("foobar");
input.add("java");
List<String> list1 = new ArrayList<>();
list1.add("hello");
list1.add("world");
List<String> list2 = new ArrayList<>();
list2.add("spring");
list2.add("multi-threaded");
Grouping g1 = new Grouping("foobar",list1);
Grouping g2 = new Grouping("java",list2);
List<Grouping> groupingList = new ArrayList<>();
groupingList.add(g1);
groupingList.add(g2);
System.out.println(mapAndMerge(input,groupingList));
}
public static List<String> mapAndMerge(List<String> input, List<Grouping> groupingList){
Set<String> returnDocs = new HashSet<>();
Iterator<String> it = input.iterator();
while(it.hasNext()){
String doc = it.next();
boolean found = false;
for (Grouping lg : groupingList){
if (lg.getName().equals(doc)){
returnDocs.addAll(lg.getList());
found=true;
}
}
if (!found){
returnDocs.add(doc);
}
}
return new ArrayList<>(returnDocs);
}
}
class Grouping {
List<String> list;
String name;
public Grouping(String name, List<String> list){
this.list=list;
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
This outputs [spring, bar, world, foo, hello, multi-threaded] which is correct.
Here is my Java 8 syntax that I tried and did NOT work;
// List<String> mergedDocs =
// input.forEach(doc->
// groupingList.stream().map( g -> g.getName().equals(doc) ? e.getList() : doc ).collect(Collectors.toList()));
// return mergedDocs;
You can make this a lot simpler by not using your Grouping class but using a simple Map<String, List<String>> instead. This map would act as the grouping, holding the list for a given name. This also enables to have a much better performance since looking into the map is constant-time (whereas your solution is in linear time since it traverses the grouping to find a matching one).
If you have to use the List<Grouping>, you can still pre-process it to convert into an intermediate Map:
The mapAndMerge method simply becomes:
public static List<String> mapAndMerge(List<String> input, List<Grouping> groupingList) {
Map<String, List<String>> map = groupingList.stream().collect(Collectors.toMap(Grouping::getName, Grouping::getList));
return input.stream()
.flatMap(s -> map.getOrDefault(s, Arrays.asList(s)).stream())
.collect(Collectors.toList());
}
Each input is flat mapped to the list contained in the map or a default list containing the current element. Then this is collected to a new list. This code prints:
[foo, bar, hello, world, spring, multi-threaded]
You can re-write the mapAndMerge method following way using java 8. But it is not very concise as you like.
public static List<String> mapAndMerge(List<String> input,
List<Grouping> groupingList) {
Set<String> returnDocs = input
.stream()
.map(t -> groupingList
.stream()
.filter(g -> g.getName().equals(t))
.map(v -> v.getList())
.findAny()
.orElse(Arrays.asList(t)))
.flatMap(t -> t.stream())
.collect(Collectors.toSet());
return new ArrayList<>(returnDocs);
}
I think it would be much simple and clearer if you use Map instead of the Grouping class.
So that's what you'll have in the main() method:
Map<String, List<String>> groupingMap = new HashMap<>();
groupingMap.put("foobar", list1);
groupingMap.put("java", list2);
List<String> mergedDocs = new ArrayList<>();
input.stream()
.map(doc -> groupingMap.getOrDefault(doc, Collections.singletonList(doc)))
.forEach(mergedDocs::addAll);
System.out.println(mergedDocs);