Passing BiPredicate to Stream for Comparing List of Objects - java

A list of Journeys can only be completed by a person if the journey timetable do not overlap. e.g. this list should return true because dates don't overlap.
Journey 1: "2019-09-10 21:00" --> "2019-09-10 21:10"
Journey 2: "2019-08-11 22:10" --> "2019-08-11 22:20"
Journey 3: "2019-09-10 21:30" --> "2019-09-10 22:00"
I have created a predicate that checks if journey times overlap. I want to use this BiPredicate in a stream. What is the correct approach to this problem?
public class Journey {
public static void main(String[] args) throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("y-M-d H:m");
ArrayList<Route> routes = new ArrayList<>();
// This example should return true because there is no overlap between the routes.
routes.add(new Route(simpleDateFormat.parse("2019-09-10 21:00"), simpleDateFormat.parse("2019-09-10 21:10")));
routes.add(new Route(simpleDateFormat.parse("2019-08-11 22:10"), simpleDateFormat.parse("2019-08-11 22:20")));
routes.add(new Route(simpleDateFormat.parse("2019-09-10 21:30"), simpleDateFormat.parse("2019-09-10 22:00")));
boolean result = travelAllRoutes(routes);
System.out.println(result);
}
public static boolean travelAllRoutes(List<Route> routes) {
BiPredicate<Route, Route> predicate = (r1, r2) -> r1.getEndJourney().before(r2.getStartJourney());
// boolean result = routes.stream(); // use predicate here
return result;
}
}
class Route {
private Date startJourney, endJourney;
public Route(Date startJourney, Date endJourney) {
this.startJourney = startJourney;
this.endJourney = endJourney;
}
public Date getStartJourney() {
return startJourney;
}
public void setStartJourney(Date startJourney) {
this.startJourney = startJourney;
}
public Date getEndJourney() {
return endJourney;
}
public void setEndJourney(Date endJourney) {
this.endJourney = endJourney;
}
}

Don't use Stream there are not useful here, a simple for-loop is perfect
public static boolean travelAllRoutes(List<Route> routes) {
Route lastRoute = null;
routes.sort(Comparator.comparing(Route::getStartJourney));
for (Route r : routes) {
if (lastRoute == null) {
lastRoute = r;
continue;
}
if (lastRoute.getEndJourney().after(r.getStartJourney()))
return false;
lastRoute = r;
}
return true;
}
}
Also I'd suggest to use java.time.LocalDate instead of the old java.util.Date

Related

How to implement equals method which works on generics

I have the following problem. I have the following classes
public class Main {
public static void main(String[] args) {
// Expected: true
// Results in: true
List<String> strLst = new ArrayList<>();
strLst.add(new String("A"));
System.out.println(strLst.contains(new String("A")));
// Expected: true
// Results in: true
List<Number> numLst = new ArrayList<>();
numLst.add(new Number(1));
System.out.println(numLst.contains(new Number(1)));
// Expected: true
// Results in: false
Container<String> integers = new Container<>();
integers.add(new String("A"));
System.out.println(integers.contains(new String("A")));
// Expected: true
// Results in: false
Container<Number> numbers = new Container<>();
numbers.add(new Number(1));
System.out.println(numbers.contains(new Number(1)));
}
}
class Container<T> {
ArrayList<Node<T>> internal = new ArrayList<>();
public void add(T elem) {
internal.add(new Node<>(elem));
}
public boolean contains(T label) {
for (Node<T> e : internal) {
if (e.getLabel().equals(e)) {
return true;
}
}
return false;
}
}
class Node<T> {
T label;
public Node(T label) {
this.label = label;
}
public T getLabel() {
return label;
}
}
class Number {
private int val;
public Number(int val) {
this.val = val;
}
public boolean equals(Object o) {
if (o instanceof Number) {
return this.val == ((Number) o).val;
}
return false;
}
}
I am not sure what went wrong here. I would expect similar behavior with my Container as with ArrayList. But it does not, because in my opinion ArrayList (see https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/ArrayList.java#L251) implements contains in a similar way.
What does ArrayList do different than I do? And if it is not possible to implement such behavior the way I hope to, how would I do this alternatively?
The line
if (e.getLabel().equals(e)) {
in contains should be
if (e.getLabel().equals(label)) {
Solution:
public boolean contains(T label) {
for (Node<T> e : internal) {
if (e.getLabel().equals(label)) {
return true;
}
}
return false;
}

Flink: getRecord() not getting called in AggregateFunction

I am trying to create a TumblingWindow on a stream of continuous data and create aggregates within the window. But for some reason, the getResult() does not get called.
public class MyAggregator implements AggregateFunction<Event, MyMetrics, MyMetrics> {
#Override
public MyMetrics createAccumulator() {
return new MyMetrics(0L, 0L);
}
#Override
public MyMetrics add(Event value, MyMetrics accumulator) {
Instant previousValue = ...;
if (previousValue != null) {
Long myWay = ...;
accumulator.setMyWay(myWay);
}
return accumulator;
}
#Override
public MyMetrics getResult(MyMetrics accumulator) {
System.out.println("Inside getResult()");
return accumulator;
}
#Override
public MyMetrics merge(MyMetrics acc1, MyMetrics acc2) {
return new MyMetrics(
acc1.getMyWay() + acc2.getMyWay());
}
}
Note: event.getClientTime() returns an Instant object.
private WatermarkStrategy getWatermarkStrategy() {
return WatermarkStrategy
.<MyEvent>forBoundedOutOfOrderness(Duration.ofMinutes(10))
.withTimestampAssigner(
(event, timestamp) ->
event.getClientTime().toEpochMilli()
);
}
public static void main(String[] args) {
DataStream<MyEvent> watermarkedData = actuals
.assignTimestampsAndWatermarks(
getWatermarkStrategy()
).name("addWatermark");
final OutputTag<MyEvent> lateOutputTag = new OutputTag<MyEvent>("late-data"){};
SingleOutputStreamOperator<OutputModel> output_data = watermarkedData
.keyBy("input_key")
.window(TumblingEventTimeWindows.of(Time.hours(1)))
.sideOutputLateData(lateOutputTag)
.aggregate(
new MyAggregator(),
).name("AggregationRollUp");
output_data.addSink(new PrintSinkFunction<>());
}
Any pointers as to what I am missing here would be helpful.
First check the timing of the data to see if it meets the window trigger conditions
Second may be you can do a test by reducing the window size from 1h to 1min and reducing the watermark region from 10min to 30s

How to find latest feedback given by each user from list of feedbacks in least order?

Feedback looks like this:
public FeedbackTable(String clusterKey, Date eventStartTime, String user, Date feedbackReceivedTime, int feedback)
{
this.id=clusterKey;
this.user=user;
this.feedback=feedback;
this.feedbackReceivedTime=feedbackReceivedTime;
this.eventStartTime = eventStartTime;
}
I get a list of these feedbacks and I want a list of feedbacks which has only the latest feedbackReceivedTime for each user.
I can iterate through the feedback list and find unique users and then for each user iterate over the feedback list to get the latest feedback but it's not least order.
Here's a way how to do it using Stream API:
List<FeedbackTable> feedbacks = ...; // your list of feedbacks
List<FeedbackTable> latestUsersFeedbacks
= feedbacks.stream()
.collect(Collectors.toMap(f -> f.user,
f -> f,
(f1, f2) -> f1.feedbackReceivedTime.after(f2.feedbackReceivedTime) ? f1 : f2))
.values().stream().collect(Collectors.toList());
You can use streams to solve the problem:
List<FeedbackTable> rawFeedbacks;
List<FeedbackTable> newestFeedbacks =
rawFeedbacks
.stream()
.collect(Collectors.groupingBy(FeedbackTable::getUser))
.entrySet().stream()
.map(a -> a.getValue().stream().max(Comparator.comparing(FeedbackTable::getFeedbackReceivedTime)).get())
.collect(Collectors.toList());
If you have control over the FeebbackTable class, then the best is to override its equals and hashcode methods and implement java.lang.Comparable#compareTo. Like this,
public class FeedbackTable implements Comparable<FeedbackTable> {
private final String id;
private final String user;
private final int feedback;
private final Date feedbackReceivedTime;
private final Date eventStartTime;
public FeedbackTable(
String clusterKey,
Date eventStartTime,
String user,
Date feedbackReceivedTime,
int feedback) {
this.id = clusterKey;
this.user = user;
this.feedback = feedback;
this.feedbackReceivedTime = feedbackReceivedTime;
this.eventStartTime = eventStartTime;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FeedbackTable that = (FeedbackTable) o;
return id.equals(that.id) && user.equals(that.user);
}
#Override
public int hashCode() {
return Objects.hash(id, user);
}
#Override
public int compareTo(FeedbackTable that) {
if (this.user.equals(that.user)) {
if (this.feedbackReceivedTime.before(that.feedbackReceivedTime)) {
return 1;
} else if (this.feedbackReceivedTime.after(that.eventStartTime)) {
return -1;
} else {
return 0;
}
}
return this.user.compareTo(that.user);
}
}
Later on, simply use the stream API,
feedbackTables.stream().sorted().distinct().collect(Collectors.toList());
And you are done.
You could use a HashMap with each user as a key and update the user's latest feedback (FeedbackTable) value accordingly:
public ArrayList<FeedbackTable> getLatestFeedbacksForUsers(List<FeedbackTable> feedbackTableList) {
Map<String, FeedbackTable> latestFeedbackMap = new HashMap<>();
for (FeedbackTable ft : feedbackTableList) {
if (latestFeedbackMap.containsKey(ft.user)) {
if (latestFeedbackMap.get(ft.user).feedbackReceivedTime.before(ft.feedbackReceivedTime)) {
latestFeedbackMap.put(ft.user, ft);
}
} else {
latestFeedbackMap.put(ft.user, ft);
}
}
List<FeedbackTable> latestFeedbacks = new ArrayList<FeedbackTable>(latestFeedbackMap.values());
// Sort Feedbacks in descending order
Collections.sort(latestFeedbacks, new Comparator<FeedbackTable>() {
#Override
public int compare(FeedbackTable f1, FeedbackTable f2) {
return f2.feedbackReceivedTime().compareTo(f1.feedbackReceivedTime());
}
});
return latestFeedbacks;
}
it is not 100% what You are looking for
Map<String, Optional<FeedbackTable>> datesMap = tables.stream().collect(
Collectors.groupingBy( (ft) -> ft.user, Collectors.reducing(
(ft1, ft2) -> ft1.feedbackReceivedTime.compareTo( ft2.feedbackReceivedTime ) > 0 ? ft1 : ft2 ) ) );
so You need this, to get the date for a user:
datesMap.get( "username" ).get().feedbackReceivedTime
but You don't need a getFeedbackReceivedTime() method

CacheLoader loading the same keys in multiple times

I am getting duplicate keys in my cacheIterator.
I'm calling a web service using SOAP to rate policies for an insurance company. I am attempting to use a Cachebuilder / loader to store the DTO's as a key and the response from the service as a value. From what I've researched, the .get and .getUnchecked methods will get a value from the cache and if it's not there, it will load that value into the cache.
here is some code:
public class CacheLoaderImpl
{
private static CacheLoaderImpl instance = null;
private static LoadingCache<PolicyDTO, RatingServiceObjectsResponse> responses;
protected CacheLoaderImpl()
{
responses = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<PolicyDTO, RatingServiceObjectsResponse>() {
public RatingServiceObjectsResponse load(PolicyDTO key)
throws Exception
{
return getResponse(key);
}
});
}
public static CacheLoaderImpl getIntance()
{
if(instance == null)
{
instance = new CacheLoaderImpl();
}
return instance;
}
public LoadingCache<PolicyDTO, RatingServiceObjectsResponse> getResponses()
{
return responses;
}
public RatingServiceObjectsResponse getResponse(PolicyDTO key) throws ExecutionException
{
RatingServiceObjectsResponse response = new RatingServiceObjectsResponse();
try
{
response = new CGIRatabaseServiceImpl().getCoverages(key);
}
catch (RemoteException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
return response;
}
}
And this is where I call the get method:
RatingServiceObjectsResponse response = CacheLoaderImpl.getIntance().getResponses().get(policy.toCoveragesCallDTO());
I was under the assumption that maybe it was comparing memory addresses which would be different so I overwrote the toString method to convert the DTO object to JSON. Upon inspecting the cache I can see that the keys are exactly the same with a compare tool. Yet, they're still being stored and calling the service every single time. I tried overwriting the equals method on PolicyDTO but it is never hit when I debug.
How can I make the cacheloader only load values of different keys and pull existing values out as it is originally intended?
I think I just don't have a solid idea how the cacheLoader actually works. I appreciate any help or suggestions.
PolicyDTO class:
public class PolicyDTO extends AbstractDto implements IPolicyDTO
{
private ArrayList<ILineOfBusinessDTO> lobDTOs = new ArrayList<ILineOfBusinessDTO>();
private String pcInd;
private String ratingEffectiveDate;
private String companyName;
public String getPcInd()
{
return pcInd;
}
public void setPcInd(String pcInd)
{
this.pcInd = pcInd;
}
public String getRatingEffectiveDate()
{
return ratingEffectiveDate;
}
public void setRatingEffectiveDate(AdvancedDate ratingEffectiveDate)
{
if(ratingEffectiveDate != null)
{
this.ratingEffectiveDate = ratingEffectiveDate.toFormattedStringMMDDYYYY();
}
else
{
this.ratingEffectiveDate = new AdvancedDate().toFormattedStringMMDDYYYY();
}
}
public String getCompanyName()
{
return companyName;
}
public void setCompanyName(String companyName)
{
this.companyName = companyName;
}
public DtoType getType()
{
return hasGetCoveragesCoverageDTO() ? DtoType.GET_COVERAGE_POLICY : DtoType.RATE_POLICY;
}
public boolean hasGetCoveragesCoverageDTO()
{
if(lobDTOs != null)
{
for(ILineOfBusinessDTO lineDTO : lobDTOs)
{
if(lineDTO.hasGetCoveragesCoverageDTO())
{
return true;
}
}
}
return false;
}
#Override
public void addLob(ILineOfBusinessDTO lob) {
lobDTOs.add(lob);
}
#Override
public Iterator<ILineOfBusinessDTO> getLobIterator() {
return lobDTOs.iterator();
}
public ICoverageDTO findCoverage(String coverageID)
{
ICoverageDTO coverageDTO = null;
for(ILineOfBusinessDTO lineDTO : lobDTOs)
{
coverageDTO = lineDTO.findCoverage(coverageID);
if(coverageDTO != null)
{
return coverageDTO;
}
}
return null;
}
#Override
public String toString()
{
return JSONConversionUtility.convertPolicyDTO(this);
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ ((companyName == null) ? 0 : companyName.hashCode());
result = prime * result + ((lobDTOs == null) ? 0 : lobDTOs.hashCode());
result = prime * result + ((pcInd == null) ? 0 : pcInd.hashCode());
result = prime
* result
+ ((ratingEffectiveDate == null) ? 0 : ratingEffectiveDate
.hashCode());
return result;
}
#Override
public boolean equals(Object object)
{
if(object instanceof PolicyDTO)
{
return object.toString().equals(this.toString());
}
return false;
}
}
Your PolicyDTO class has hashCode inconsistent with equals - it violates the following rule:
If two objects are equal according to the equals(Object) method, then
calling the hashCode method on each of the two objects must produce
the same integer result.
Cache uses hashCode (much like HashMap class does), so when it sees two keys with different hashcodes, it assumes they are not equal.

How can I combine the results of two separate WS calls in Play framework?

I have a controller method which sends two web service requests at the same time, I immediately return a promise for both of them. Now what I want to do is combine the results of the two web service calls into a single result returned to the user. The code I have so far is:
public static Promise<Result> search(String searchTerms) {
final Promise<List<SearchResult>> result1 = webserviceOne(searchTerms);
final Promise<List<SearchResult>> result2 = webserviceTwo(searchTerms);
return result1.flatMap(
new Function<Promise<List<SearchResult>>, Promise<Result>>() {
public Promise<Result> apply(Promise<List<SearchResult>> res1) {
return result2.flatMap(
new Function<Promise<List<SearchResult>>, Result>() {
public Result apply(Promise<List<SearchResult>> res2) {
//TODO: Here I want to combine the two lists of results and return a JSON response
}
}
);
}
}
);
}
How do I do this? I'm finding it really hard to find decent documentation for this sort of thing.
Something like this should do it:
public static Promise<Result> search(String searchTerms) {
final Promise<List<SearchResult>> result1 = webserviceOne(searchTerms);
final Promise<List<SearchResult>> result2 = webserviceTwo(searchTerms);
return result1.flatMap(
new Function<Promise<List<SearchResult>>, Promise<Result>>() {
public Promise<Result> apply(List<SearchResult> res1) {
return result2.flatMap(
new Function<Promise<List<SearchResult>>, Result>() {
public Result apply(List<SearchResult> res2) {
List<SearchResult> newList = new ArrayList<SearchResult>(res1);
newList.addAll(res2);
return ok(toJson(newList));
}
}
);
}
}
);
}
#Override
public Zone buildZone(final GeoPoint center, final int distance) {
Promise<List<Street>> streetPromise = Promise.promise(
new Function0<List<Street>>() {
public List<Street> apply() {
return streetRepository.findByLocation(center.getGeom(), distance);
}
}
);
Promise<List<Place>> placePromise = Promise.promise(
new Function0<List<Place>>() {
public List<Place> apply() {
return placeService.findByLocation(center, distance);
}
}
);
Promise<Zone> result = Promise.sequence(streetPromise, placePromise).map(
new Function<List<List<? extends Object>>, Zone>() {
#Override
public Zone apply(List<List<? extends Object>> lists) throws Throwable {
return new Zone((List<Street>) lists.get(0), (List<Place>) lists.get(1));
}
}
);
return result.get(10, TimeUnit.SECONDS);
}

Categories