I have small piece of code (POJO) with java-8. My question is can I do something better with using java-8? I have mentioned some comments in class.
Assume that I have Person, Standard and Subject pojos and also do not worry about equals and hashCode methods, I just want inputs with respect to Java-8.
package com.java8.learn.domain;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* #author Vishal.Zanzrukia
*
*/
public class Teacher extends Person {
private Map<Standard, Set<Subject>> standardWiseSubjects;
public Map<Standard, Set<Subject>> getStandardWiseSubjects() {
return standardWiseSubjects;
}
public void addStandardWiseSubject(Subject subject, Standard standard) {
if (this.standardWiseSubjects == null) {
this.standardWiseSubjects = new HashMap<>();
}
if (this.standardWiseSubjects.containsKey(standard)) {
this.standardWiseSubjects.get(standard).add(subject);
} else {
Set<Subject> subjects = new HashSet<>();
subjects.add(subject);
this.standardWiseSubjects.put(standard, subjects);
}
}
// I need some inputs over here.
public Map<Subject, Set<Standard>> getSubjectWiseStandards() {
if (this.standardWiseSubjects == null) {
return null;
}
Map<Subject, Set<Standard>> output = new HashMap<>();
this.standardWiseSubjects.values().parallelStream().flatMap(set -> set.stream()).forEach(subject -> {
Set<Standard> standards = new HashSet<>();
this.standardWiseSubjects.forEach((standard, subjects) -> {
// TODO can I convert this if condition into predicate?
if (subjects.contains(subject)) {
standards.add(standard);
}
});
output.put(subject, standards);
});
return output;
}
}
The addStandardWiseSubject method can be simplified using the computeIfAbsent method:
public void addStandardWiseSubject(Subject subject, Standard standard) {
if (this.standardWiseSubjects == null) {
this.standardWiseSubjects = new HashMap<>();
}
this.standardWiseSubjects.computeIfAbsent(standard,
s -> new HashSet<>()).add(subject);
}
The getSubjectWiseStandards() is trickier: you need to invert the map. It can be done producing the intermediate pairs of all subjects and corresponding standards (I used the AbstractMap.SimpleEntry class to represent such pair):
public Map<Subject, Set<Standard>> getSubjectWiseStandards() {
if (this.standardWiseSubjects == null) {
return null;
}
return this.standardWiseSubjects.entrySet().parallelStream()
.<Map.Entry<Subject, Standard>> flatMap(
e -> e.getValue().stream()
.map(st -> new AbstractMap.SimpleEntry<>(st, e.getKey())))
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(e -> e.getValue(),
Collectors.toSet())));
}
This may look simpler using the EntryStream class of my StreamEx library:
public Map<Subject, Set<Standard>> getSubjectWiseStandards() {
if (this.standardWiseSubjects == null) {
return null;
}
return EntryStream.of(this.standardWiseSubjects).parallel()
.invert().flatMapKeys(Set::stream).groupingTo(HashSet::new);
}
Please note that forEach with parallel stream should be used with extreme care as it can be executed for different elements in parallel. Your solution may work incorrectly as you modify the HashMap (which is not thread-safe) from this forEach operation.
It should be possible like below
Map<Standard, Set<Subject>> map = new HashMap<Standard, Set<Subject>>();
Map<Subject, Set<Standard>> reverseMap = new HashMap<Subject, Set<Standard>>();
HashSet<Subject> std1Subjects = new HashSet<Subject>();
std1Subjects.add(new Subject("English"));
std1Subjects.add(new Subject("Maths"));
std1Subjects.add(new Subject("Science"));
HashSet<Subject> std2Subjects = new HashSet<Subject>();
std2Subjects.add(new Subject("Hindi"));
std2Subjects.add(new Subject("Gujarati"));
std2Subjects.add(new Subject("Maths"));
HashSet<Subject> std3Subjects = new HashSet<Subject>();
std3Subjects.add(new Subject("Sanskrit"));
std3Subjects.add(new Subject("Science"));
std3Subjects.add(new Subject("Maths"));
map.put(new Standard("1"), std1Subjects);
map.put(new Standard("2"), std2Subjects);
map.put(new Standard("3"), std3Subjects);
//1.
map.keySet().forEach((std) -> map.get(std).forEach((sub)->{
if(reverseMap.containsKey(sub)){
reverseMap.get(sub).add(std);
}else{
HashSet<Standard> standardSet = new HashSet<Standard>();
standardSet.add(std);
reverseMap.put(sub, standardSet);
}
}));
reverseMap
.forEach((k, v) -> System.out.println("k-" + k + " , v-" + v));
And also like below
map.keySet().forEach((std) -> map.get(std).forEach((sub)->{
reverseMap.computeIfAbsent(sub,standard-> new HashSet<>()).add(std);
reverseMap.computeIfPresent(sub, (subject,stdSet) -> reverseMap.get(subject)).add(std);
}));
Related
We obtain a list of data from a SQL Server database. I want to return the list when there is data, but when there is not, I want to return a "No Content" status. My code:
public class Main {
public static void main(String[] args) {
var main = new Main();
var result = main.controllerMethod();
System.out.println("Result: " + result.blockingGet());
}
public Flowable<Person> personList(){
List<Person> repositoryList = List.of();
return repositoryList
.stream()
.collect(Collectors.collectingAndThen(Collectors.toList(), list -> {
if(list.isEmpty()) return Flowable.empty();
else return Flowable.fromIterable(list);
}));
}
public Maybe<ResponseEntity<Flowable<Person>>> controllerMethod(){
var httpStatus =
new AtomicReference<>(HttpStatus.OK);
return Maybe.just(personList()
.switchIfEmpty(subs -> Completable.fromAction(() ->
httpStatus.set(HttpStatus.NO_CONTENT)).toFlowable()))
.map(person -> ResponseEntity.status(httpStatus.get())
.body(person));
}
}
result:
Result: <200 OK OK,io.reactivex.rxjava3.internal.operators.flowable.FlowableSwitchIfEmpty#35f983a6,[]>
It seems that you expect httpStatus.set(HttpStatus.NO_CONTENT) to be run, and after running, the status can be read with the correct statuscode. I'm not very familiar with reactive, but by looking at Completable.java I don't think the Action that you provide in Completable::fromAction is run right away.
There is probably a more reactive-like way to solve the problem, but I can't help you there. Instead, here's a solution that might work (or at least get you going in the right direction). Just set the value of the status explicitly yourself before returning your Flowable:
public Flowable<Person> personList(AtomicReference<HttpStatus> reference) {
List<Person> repositoryList = List.of();
return repositoryList
.stream()
.collect(Collectors.collectingAndThen(Collectors.toList(), list -> {
if (list.isEmpty()) {
reference.set(HttpStatus.NOT_FOUND);
return Flowable.empty();
}
return Flowable.fromIterable(list);
}));
}
public Maybe<ResponseEntity<Flowable<Person>>> controllerMethod() {
var httpStatus = new AtomicReference<>(HttpStatus.OK);
return Maybe.just(personList(httpStatus))
.map(person -> ResponseEntity.status(httpStatus.get()))
.body(person));
}
I am new to reactive world and trying to write for following logic:
find if entry is there in database corresponding to my service call. If the the data is not there in database then i have to preapre that vendorServiceAreaMapping object and then save.
VendorServiceAreaMapping vendorServiceAreaMapping = new VendorServiceAreaMapping();
vendorServiceAreaMapping.setVendorId(123);
vendorServiceAreaMapping.setClientId(456);
int count= vendorService.findCountByVendorId(vendorServiceAreaMapping.getVendorId(), vendorServiceAreaMapping.getClientId())
if(count ==0){
CityModel cityModel = cityService.getCityByName("Florida");
vendorServiceAreaMapping.setCityId(cityModel.getCityId());
vendorService.save(vendorServiceAreaMapping);
}else{
new VendorServiceAreaMapping();
}
Above code snippet is what i am trying to incorporate using spring reactive way:
public Mono<VendorServiceAreaMapping> createVendorMapping(String invitationName) {
return invitationService.getInvitationDetails(invitationName)
.flatMap(vendorServiceAreaMapping -> {
vendorService.findCountByVendorId(vendorServiceAreaMapping.getVendorId(), vendorServiceAreaMapping.getClientId())// returns integer
.doOnNext(count -> {
if(count == 0){// there is no corresponding entry in database
.flatMap(vendorServiceAreaMapping -> {
return cityService.getCityByName("Florida")
.map(cityModel -> {
vendorServiceAreaMapping.setCityId(cityModel.getCityId());
return vendorServiceAreaMapping;
});
})
.flatMap(vendorServiceAreaMapping -> {
return vendorService.save(vendorServiceAreaMapping);
})
}else{
return Mono.just(new VendorServiceAreaMapping());
}
});
}
}
You just need to create a reactive flow chaining async methods using flatMap.
public Mono<VendorServiceAreaMapping> createVendorMapping(String invitationName) {
return invitationService.getInvitationDetails(invitationName)
.flatMap(vendorServiceAreaMapping ->
vendorService.findCountByVendorId(vendorServiceAreaMapping.getVendorId(), vendorServiceAreaMapping.getClientId())
.flatMap(count -> {
if (count == 0) {
return cityService.getCityByName("Florida")
.flatMap(cityModel -> {
vendorServiceAreaMapping.setCityId(cityModel.getCityId());
return vendorService.save(vendorServiceAreaMapping);
});
} else {
return Mono.just(new VendorServiceAreaMapping());
}
})
);
}
This code assumes that all methods are reactive and return Mono<T>.
Learning RxJava so will be appreciate for any advice.
Need to receive objects when calculation is ready, so I made PublishSubject in models method:
public PublishSubject<BaseUnit> exec(int inputNumber) {
if (unitList.size() > 0) {
for (BaseUnit unit : unitList) {
unit.setInProgress();
}
}
PublishSubject<BaseUnit> subject = PublishSubject.create();
list = new ArrayList<>();
populateList(inputNumber).
subscribeOn(Schedulers.from(Executors.newFixedThreadPool(ThreadPool.getPoolSize())))
.subscribe(calculatedList -> {
list = calculatedList;
for (List<Integer> elem : list) {
for (ListOperationName operationName : ListOperationName.values()) {
ListUnit unit = new ListUnit(operationName, elem, 0);
calculate(unit);
unitList.add(unit);
subject.onNext(unit);
}
}
}, error -> Log.d("ERROR", error.toString()));
return subject;
}
public Observable<ArrayList<List<Integer>>> populateList(int inputNumber) {
return Observable.fromCallable(() -> {
ArrayList<List<Integer>> list = new ArrayList<>();
Integer[] populatedArray = new Integer[inputNumber];
Arrays.fill(populatedArray, insertValue);
list.add(new ArrayList<>(Arrays.asList(populatedArray)));
list.add(new LinkedList<>(Arrays.asList(populatedArray)));
list.add(new CopyOnWriteArrayList<>(Arrays.asList(populatedArray)));
return list;
});
}
And then trying to subscribe in presenter:
public void calculate(int inputNumber) {
fragment.showAllProgressBars();
repository.exec(inputNumber)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.from(Executors.newFixedThreadPool(ThreadPool.getPoolSize())))
.subscribe(unit -> {
Log.d("PRESENTER RESULT", unit.toString());
fragment.setCellText(unit.getViewId(), unit.getTimeString());
}, error -> Log.d("PRESENTER ERROR", error.toString()));
}
This gives me nothing. But if I use ReplaySubject - it gives me all results, but seems like it uses only one thread. So I assume there is somethings wrong I made with subscription and it should be somewhere earlier.
I need to use exactly PublishSubject for giving me results as they are ready using multiple threads.
How to fix that? Or maybe there is other problem?
I would like to auto complete using more than one word, for example:
> we can<TAB>
welcome_trashcan pecan_seaweed yeswecan canwest
So all the suggestions should contain both of the keywords. Ideally, it should work for unlimited keywords.
I read the completion wiki, but I don't know which path to follow to achieve this.
I ended up implementing a new Interface (its written in groovy):
class MatchAnyCompleter implements Completer {
protected final List<Candidate> candidateList = []
MatchAnyCompleter(List<String> list) {
assert list
list.each {
candidateList << new Candidate(AttributedString.stripAnsi(it), it, null, null, null, null, true)
}
}
#Override
void complete(final LineReader reader, final ParsedLine commandLine, final List<Candidate> selected) {
assert commandLine != null
assert selected != null
selected.addAll(candidateList.findAll {
Candidate candidate ->
commandLine.words().stream().allMatch {
String keyword ->
candidate.value().contains(keyword)
}
})
}
}
Test:
class MatchAnyCompleterTest extends Specification {
def "Testing matches"() {
setup:
def mac = new MatchAnyCompleter([
"somevalue",
"welcome_trashcan",
"pecan_seaweed",
"yeswecan",
"canwest",
"nomatchhere"
])
def cmdLine = new ParsedLine() {
// non-implemented methods were removed for simplicity
#Override
List<String> words() {
return ["we","can"]
}
}
List<Candidate> selected = []
mac.complete(null, cmdLine, selected)
expect:
selected.each {
println it.value()
}
assert selected.size() == 4
}
}
output:
welcome_trashcan
pecan_seaweed
yeswecan
canwest
I have two requests, second one it dependence in the First so, how to make it in sequence, because there is some check will receive null if it request in parallel
Observable<Map<Integer, SupportedVersion>> supportedVersionObservable = contentAPI
.getSupportedVersionsContent()
.compose(ReactiveUtils.applySchedulers())
.map(supportedVersionsContentContentContainer -> supportedVersionsContentContentContainer.getContent().get(0).getMessage())
.doOnNext(supportedVersionsMap -> {
Timber.i("doOnNext invoked from supported version observable");
for (Map.Entry<Integer,SupportedVersion> entry : supportedVersionsMap.entrySet())
if (Build.VERSION.SDK_INT >= entry.getKey())
model.setSupportedVersion(entry.getValue());
model.setCurrentVersionExpiryDate(model.getSupportedVersion().getCurrentVersionExpiryDate());
if (model.getSupportedVersion() != null)
model.setNewFeaturesSeen(sharedPreferencesManager.isNewFeaturesSeen(model.getSupportedVersion().getAvailableVersions().get(0)));
if (model.isNewFeaturesSeen());
//request data from here
})
.retry(1);
Observable<List<WhatsNew>> getWhatsNewFeature = contentAPI
.getWhatsNewFeature(model.getSupportedVersion().getAvailableVersions().get(0))
.compose(ReactiveUtils.applySchedulers())
.doOnNext(whatsNewList -> {
Timber.i("doOnNext invoked from supported version observable");
if (!whatsNewList.isEmpty())
model.setWhatsNews(whatsNewList);
})
.retry(1);
You can use flatMap for that:
public Observable<List<WhatsNew>> makeRequest {
return contentAPI
.getSupportedVersionsContent()
.flatMap(supportedVersionsMap -> {
//... model initialization
return contentAPI
.getWhatsNewFeature(model.getSupportedVersion().getAvailableVersions().get(0))
.compose(ReactiveUtils.applySchedulers())
.doOnNext(whatsNewList -> {
Timber.i("doOnNext invoked from supported version observable");
if (!whatsNewList.isEmpty())
model.setWhatsNews(whatsNewList);
})
.retry(1);
});
You do not need side-effects. You may hold the model-state in the operators:
#Test
void name() {
ContentApi mock = mock(ContentApi.class);
Observable<Model> modelObservable = mock.getSupportedVersionsContent()
.map(s -> {
// do Mapping
return new Model();
})
.flatMap(model -> mock.getWhatsNewFeature(model)
.map(whatsNews -> {
// Create new model with whatsNews
return new Model();
}), 1);
}
interface ContentApi {
Observable<String> getSupportedVersionsContent();
Observable<List<WhatsNew>> getWhatsNewFeature(Model model);
}
class Model {
}
class WhatsNew {
}
Please have a Look for a detail description of flatMap:
http://tomstechnicalblog.blogspot.de/2015/11/rxjava-operators-flatmap.html?m=0