Looping within multiple arrays in Java Stream - java

I would need some help optimising the way to iterate through a list of 200 lines.
My 1st class is as below
Class Vehicle
Date date
int nbOfVehicleArriving
Class Planning
int date
int nbOfallowedVehicles
So, I have a list of Vehicles arriving per time ex:
01/01/2018 00:00:00 12
01/01/2018 00:10:00 10
01/01/2018 01:00:00 5
01/01/2018 01:10:00 10
....
And I have some agenda
01/01/2018 00:00:00 3
01/01/2018 00:10:00 2
01/01/2018 01:00:00 3
01/01/2018 01:10:00 5
I need to calculate the number of vehicles in the queue.
What i'm planning to do is to iterate with the Vehicles and then in it iterate with the Planning.
Is there a better way of doing this??
Thanks in advance

You could also use flatMap to merge the two arrays together and then simply extract the counts (this would involve casting) and sum these up.
Here is a full working example demonstrating how you can use flatMap:
import java.util.Arrays;
import java.util.Date;
import java.util.stream.Stream;
public class MergeStreams {
public static void main(String[] args) {
Vehicle[] vehicles = {new Vehicle(new Date(), 10), new Vehicle(new Date(), 11)};
Planning[] plannings = {new Planning(new Date(), 5), new Planning(new Date(), 12)};
int sum = Stream.of(vehicles, plannings)
.flatMap(Arrays::stream)
.map(object -> object instanceof Vehicle ? ((Vehicle) object).getNbOfVehicleArriving() : ((Planning) object).getNbOfallowedVehicles())
.mapToInt(Integer::intValue)
.sum();
System.out.println(sum);
}
}
class Vehicle {
private Date date;
private int nbOfVehicleArriving;
public Vehicle(Date date, int nbOfVehicleArriving) {
this.date = date;
this.nbOfVehicleArriving = nbOfVehicleArriving;
}
public int getNbOfVehicleArriving() {
return nbOfVehicleArriving;
}
}
class Planning {
private Date date;
private int nbOfallowedVehicles;
public Planning(Date date, int nbOfallowedVehicles) {
this.date = date;
this.nbOfallowedVehicles = nbOfallowedVehicles;
}
public int getNbOfallowedVehicles() {
return nbOfallowedVehicles;
}
}
If you run this example it will output 38 on the console.

For 200 lines it probably doesn't make any difference what solution you choose. But if you want a solution which scales to a very long list the correct approach is to zip the two lists (or streams) together (this way you avoid looping twice), and use a function to produce the combined result. Guava itself provides a zip() method, but you can also write your own.
Not sure what you mean by 'number of vehicles in the queue', but I am going to assume that you want to know how many were left in the end that have not been allowed in yet.
You can create your own class which maps the vehicles arriving with the agenda for that date.
public class VehiclePlanningData {
private final Vehicle vehicle;
private final Planning planning;
public VehiclePlanningData(Vehicle vehicle, Planning planning) {
this.vehicle = vehicle;
this.planning = planning;
}
public Vehicle getVehicle() {
return vehicle;
}
public Planning getPlanning() {
return planning;
}
}
Once you have this you can easily do a reduce() operation to carry forward any vehicles left over from the previous slot.
So something like this (using the Guava Streams class):
int queue = Streams.zip(vehicles.stream(), planning.stream(), (v, p) -> new VehiclePlanningData(v, p))
.reduce(0, (queue, nextSlot) ->
queue + (nextSlot.getVehicle().getNbOfVehicleArriving()
- nextSlot.getPlanning().getNbOfallowedVehicles(),
(q1, q2) -> q1 + q2);
UPDATE:
Seems that the queue is per time slot. In that case you might want to have a special class like this, which stores the queue size per slot:
public class TimeslotQueue {
private final Date date;
private final int queueSize;
public VehicleQueue(Date date, int queueSize) {
this.date = date;
this.queueSize = queueSize;
}
public Date getDate() {
return date;
}
public int getQueueSize() {
return queueSize;
}
}
Then get a stream of these classes like this:
List<TimeslotQueue> queues = Streams.zip(vehicles.stream(), planning.stream(),
(v, p) -> new TimeslotQueue(v.getDate(),
v.getNbOfVehicleArriving() - p.getNbOfallowedVehicles()))
.collect(Collectors.toList());
This way you have a list of queued vehicles for each date. As a side note, I would use the Java 8 LocalDateTime not the old Date class.

You may try this out.
Map<Date, Integer> agendasByDate = agendas.stream()
.collect(Collectors.toMap(Planning::getDate, Planning::getNbOfallowedVehicles));
Map<Date, Integer> vehiclesInQueueByDate = vehicles.stream().collect(Collectors.toMap(Vehicle::getDate,
v -> v.getNbOfVehicleArriving() - agendasByDate.get(v.getDate())));

Related

Priority Queue of user defined objects

I wanna create priority queue of objects according to their attributes. how can I compare the queue according to spesific attributes. for example if i type somewhere id, queue is designed for id. i have two different java file below. I wanna get all possible queues by comparing attributes of objects.
first
public class customers {
int id;
String name;
int age;
double money;
public customers(int id, String name, int age, double money) {
this.id = id;
this.name = name;
this.age = age;
this.money = money;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
main file starts now
import java.util.ArrayList;
import java.util.PriorityQueue;
public class draft {
public static void main(String[] args) {
PriorityQueue<customers> customerList= new PriorityQueue<>();
customers customer1= new customers(0,"jack",30,180.5);
customers customer2= new customers(1,"john",52,800.3);
customers customer3= new customers(2,"alice", 41, 400.5);
customerList.add(customer1);
customerList.add(customer2);
customerList.add(customer3);
for (customers c:customerList
) {
System.out.println(c.getId());
}
}
}
I want a simple solution like this. the code below is not valid but i can not understand complex codes. and i think there has to be a simple solution as this.
PriorityQueue<customers> customerList= new PriorityQueue<>(customers.age);
i do not know if i explained the question. for example if i compare objects id, the queue should be object#0,object#1,object#2 in this order. if i compare objects's age the queue will be object#0, object#2,object#1
Here are few ways to maintain a PriorityQueue based on any field of a particular class.
Below is an illustration to define a PriorityQueue sorted according to the field id (sorted in ascending order):
public static void main(String[] args)
{
// Method 1] Using Custom Comparator
PriorityQueue<Customers> pq1 = new PriorityQueue<Customers>(Comparator.comparing(Customers::getId));
Customers customer1 = new Customers(0,"jack",30,180.5);
Customers customer2 = new Customers(1,"john",52,800.3);
Customers customer3 = new Customers(2,"alice", 41, 400.5);
pq1.add(customer1);
pq1.add(customer2);
pq1.add(customer3);
// Method 2] Using Lambda Operator
PriorityQueue<Customers> pq2 = new PriorityQueue<>((x, y) -> x.id-y.id);
//Method 3] Custom Comparator again
PriorityQueue<Customers> pq3 = new PriorityQueue<>(new Comparator<Customers>()
{
#Override
public int compare(Customers a, Customers b)
{
return a.id-b.id;
}
});
pq2.addAll(pq1);
pq3.addAll(pq1);
System.out.println(pq1);
System.out.println(pq2);
System.out.println(pq3);
}
Output:
[(0 , jack , 30 , 180.5), (1 , john , 52 , 800.3), (2 , alice , 41 , 400.5)]
[(0 , jack , 30 , 180.5), (1 , john , 52 , 800.3), (2 , alice , 41 , 400.5)]
[(0 , jack , 30 , 180.5), (1 , john , 52 , 800.3), (2 , alice , 41 , 400.5)]
Similarly you can design other queues based on any field of your choice.
To maintain the priority queue in reverse order (Decreasing/Descending order), we need to reverse the orders in the comprators like:
PriorityQueue<Customers> pq2 = new PriorityQueue<>((x, y) -> y.id-x.id);
Please note, for ascending it was: (x, y) -> x.id-y.id
for descending it will be: (x, y) -> y.id-x.id
Using custom Comparator:
PriorityQueue<Customers> pq3 = new PriorityQueue<>(new Comparator<Customers>()
{
#Override
public int compare(Customers a, Customers b)
{
return b.id-a.id; // reversed
}
});
And if you want to sort the priority queue based on two fields, let say age & id in such a way that if multiple age's are equal, then priority is given to id.
You can achieve the above in the following ways:
public static void main(String[] args)
{
// Method 1] Using Custom Comparator (Increasing order)
PriorityQueue<Customers> pq1 = new PriorityQueue<Customers>(Comparator.comparing(Customers::getAge).thenComparing(Customers::getId));
Customers customer1 = new Customers(0,"jack",30,180.5);
Customers customer2 = new Customers(1,"john",52,800.3);
Customers customer3 = new Customers(2,"alice", 41, 400.5);
pq1.add(customer1);
pq1.add(customer2);
pq1.add(customer3);
// Method 2] Using Lambda Operator (Increasing order)
PriorityQueue<Customers> pq2 = new PriorityQueue<>((x, y) -> (x.age == y.age) ? x.id-y.id : x.age-y.age);
//Method 3] Custom Comparator again (Reverse order)
PriorityQueue<Customers> pq3 = new PriorityQueue<>(new Comparator<Customers>()
{
#Override
public int compare(Customers a, Customers b)
{
return (a.age == b.age) ? b.id-a.id : b.age-a.age;
}
});
pq2.addAll(pq1);
pq3.addAll(pq1);
System.out.println(pq1);
System.out.println(pq2);
System.out.println(pq3);
}
For sorting based on age & money, few modifications are required as money is of double dataType. Below is how you can achieve the desired:
PriorityQueue<Customers> pq1 = new PriorityQueue<Customers>(Comparator.comparing(Customers::getAge).thenComparing(Customers::getMoney));
PriorityQueue<Customers> pq2 = new PriorityQueue<>((x, y) -> (x.age == y.age) ? Double.compare(x.money,y.money) : x.age-y.age);
PriorityQueue<Customers> pq3 = new PriorityQueue<>(new Comparator<Customers>()
{ #Override
public int compare(Customers a, Customers b)
{
return (a.age == b.age) ? Double.compare(a.money,b.money) : a.age-b.age;
}
});
Use the poll method to verify the order of elements in the priorityQueue.
All the implementations are easy to understand. But if you are having a tough time understanding it, please reach out.
The way to do this would be to use multiple Comparator classes. Comparator gives us a way to define HOW we want our objects to be compared to each other - in your case, it could be id,age etc.
public class Comparator<Customer> CustomerAgeComparator implements Comparator<Customer>() {
public int compare(Customer user1, Customer user2) {
int userPoints1 = user1.getAge();
int userPoints2 = user2.getAge();
if (userPoints1 == userPoints2)
return 0;
else if (userPoints1 > userPoints2)
return 1;
else
return -1;
}
};
The above comparator will sort customers in descending order of age.
Then you need to pass this information to your Priority Queue somehow. Luckily PriorityQueue has a constructor that allows one to do just that. Call it in the following manner :
PriorityQueue oldestCustomerFirst = PriorityQueue<String>(new CustomerAgeComparator);

Grouping Java 8. Inner grouping based on time interval from a given start time

I've the list in the following format:
int intervalInMinutes = 10;
String startTimeForGrouping = "2017-05-09T15:37:51.896+00:00";
List<MyObject> myObjList = Arrays.asList(
new MyObject("1","a","2017-05-09T15:38:51.896+00:00"),
new MyObject("1","a","2017-05-09T16:41:51.896+00:00"),
new MyObject("1","a","2017-05-09T16:49:51.896+00:00"),
new MyObject("1","a","2017-05-09T16:51:51.896+00:00"),
new MyObject("2","b","2017-05-09T17:38:51.896+00:00"),
new MyObject("2","b","2017-05-09T18:41:51.896+00:00")
);
I've got the list in the above format:
I wanted to iterate through that and perform some grouping with an interval from given start time and achieve as follows:
In the above list, the group id 1 should return 2 lists.
First 2 objects in the single list because its in the first interval from startTimeForGrouping ("2017-05-09T15:37:51.896+00:00" + intervalInMinutes (10)).
3rd and 4th in a different list because that comes in the next interval ("2017-05-09T15:47:51.896+00:00" + intervalInMinutes (10)). The same logic should repeat for id 2 as well
I have tried this
Map<String, Map<Long, List<MyObject>>> aggrResult =
myObjList.stream()
.collect(
Collectors.groupingBy(
MyObject::getId,
Collectors.groupingBy(eachQueueObj -> eachQueueObj.getIntervalStartTime()/interval)));
but this is wrong because I want to aggregate based on the interval from the given start time.
Please help me since am new to java streaming
You should parse the date strings into Instant objects, and call Duration.between to get a duration. Then, divide that duration by intervalInMinutes minutes. Finally, multiply this number by intervalInMinutes and add that to startTimeForGrouping to get the grouping key.
Before the stream operation, parse startTimeForGrouping to an instant, and make a Duration from startTimeForGrouping:
Instant startInstant = OffsetDateTime.parse(startTimeForGrouping).toInstant();
Duration interval = Duration.ofMinutes(startTimeForGrouping);
Then you can declare a method roundInstant:
public static Instant roundInstant(Instant instant, Duration interval, Instant start) {
long multiple =
Duration.between(start, instant)
.getSeconds() / interval.getSeconds();
return start.plus(interval.multipliedBy(multiple));
}
The second groupingBy call can then look like this:
Collectors.groupingBy(
eachQueueObj -> roundInstant(
OffsetDateTime.parse(eachQueueObj.getIntervalStartTime()).toInstant(),
interval,
startInstant
).getEpochSecond()
)
In fact, I would recommend you to not use Strings and Longs, and work with Instants directly.
I think the code is that,you could try and remodify the getTime method to fix your need
package com.test;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
*
*
*
*
*
*
* #author shikai.liu
* #version 1.0
* #since JDK1.7
*/
public class TestList {
public static String strDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
public static SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
public static class MyObject {
public String id;
public String code;
public String time;
public MyObject(String id, String code, String time) {
this.code = code;
this.id = id;
this.time = time;
}
public String getId() {
return this.id;
}
public String getIntervalStartTime() {
return time;
}
public long getTime(Integer t, String srcTime) {
long result = 0;
try {
Date dstDate = sdf.parse(time);
Date srcDate = sdf.parse(srcTime);
long inteval = dstDate.getTime() - srcDate.getTime();
result = inteval / (1000 * 60) / t;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
public static void main(String[] args) throws Exception {
int intervalInMinutes = 10;
String startTimeForGrouping = "2017-05-09T15:37:51.896+00:00";
List<MyObject> myObjList = Arrays.asList(new MyObject("1", "a", "2017-05-09T15:38:51.896+00:00"), new MyObject("1", "a",
"2017-05-09T16:41:51.896+00:00"), new MyObject("1", "a", "2017-05-09T16:49:51.896+00:00"), new MyObject("1", "a",
"2017-05-09T16:51:51.896+00:00"), new MyObject("2", "b", "2017-05-09T17:38:51.896+00:00"), new MyObject("2", "b",
"2017-05-09T18:41:51.896+00:00"));
Map<String, Map<Long, List<MyObject>>> aggrResult = myObjList.stream().collect(
Collectors.groupingBy(MyObject::getId, Collectors.groupingBy(a -> a.getTime(intervalInMinutes, startTimeForGrouping))));
System.out.println(1);
}
}

Getting the sum and max from a list using stream

Hi I have a List where the data looks like this
[{"month":"April","day":"Friday","count":5},
{"month":"April","day":"Monday","count":6},
{"month":"April","day":"Saturday","count":2},
{"month":"April","day":"Sunday","count":1},
{"month":"April","day":"Thursday","count":7},
{"month":"April","day":"Tuesday","count":8},
{"month":"April","day":"Wednesday","count":10},
{"month":"March","day":"Friday","count":3},
{"month":"March","day":"Monday","count":2},
{"month":"March","day":"Saturday","count":15},
{"month":"March","day":"Sunday","count":11},
{"month":"March","day":"Thursday","count":4},
{"month":"March","day":"Tuesday","count":20},
{"month":"March","day":"Wednesday","count":7},
{"month":"May","day":"Friday","count":2},
{"month":"May","day":"Monday","count":0},
{"month":"May","day":"Saturday","count":7},
{"month":"May","day":"Sunday","count":4},
{"month":"May","day":"Thursday","count":8},
{"month":"May","day":"Tuesday","count":3},
{"month":"May","day":"Wednesday","count":6}]
My object class is
String month;
String day;
Integer count;
What I want to get by using stream is sum of count grouped by month and the day with max count for that month.
so end result will look something like
April, Wednesday, 39
March, Tuesday, 62
May, Thursday , 30
I have been trying to use stream and grouping by but no luck. Any help is appreciated. Thanks
EDIT
Map<String, Integer> totalMap = transactions.stream().collect(Collectors.groupingBy(MonthlyTransaction::getMonth, Collectors.summingInt(MonthlyTransaction::getCount)));
Map<String, String> maxMap = transactions.stream().collect(Collectors.groupingBy(MonthlyTransaction::getMonth)).values().stream().toMap(Object::getDay, Collextions.max(Object::getCount);
obviously the maxMap method is wrong but I do not know how to write it.
If you want to find both the sum of counts per month and the day with the max count per month in a single pass, I think you need a custom collector.
First, let's create a holder class where to store the results:
public class Statistics {
private final String dayWithMaxCount;
private final long totalCount;
public Statistics(String dayWithMaxCount, long totalCount) {
this.dayWithMaxCount = dayWithMaxCount;
this.totalCount = totalCount;
}
// TODO getters and toString
}
Then, create this method, which returns a collector that accumulates both the sum of counts and the max count, along with the day in which that max was found:
public static Collector<MonthlyTransaction, ?, Statistics> withStatistics() {
class Acc {
long sum = 0;
long maxCount = Long.MIN_VALUE;
String dayWithMaxCount;
void accumulate(MonthlyTransaction transaction) {
sum += transaction.getCount();
if (transaction.getCount() > maxCount) {
maxCount = transaction.getCount();
dayWithMaxCount = transaction.getDay();
}
}
Acc merge(Acc another) {
sum += another.sum;
if (another.maxCount > maxCount) {
maxCount = another.maxCount;
dayWithMaxCount = another.dayWithMaxCount;
}
return this;
}
Statistics finish() {
return new Statistics(dayWithMaxCount, sum);
}
}
return Collector.of(Acc::new, Acc::accumulate, Acc::merge, Acc::finish);
}
This uses the local class Acc to accumulate and merge partial results. The finish method returns an instance of the Statistics class, which holds the final results. At the end, I'm using Collector.of to create a collector based on the methods of the Acc class.
Finally, you can use the method and class defined above as follows:
Map<String, Statistics> statisticsByMonth = transactions.stream()
.collect(Collectors.groupingBy(MonthlyTransaction::getMonth, withStatistics()));
did this in 2 steps instead of trying to write 1 stream to achieve the result
//First get the total of counts grouping by month
Map<String, Integer> totalMap = transactions.stream()
.collect(Collectors.groupingBy(MonthlyTransaction::getMonth, Collectors.summingInt(MonthlyTransaction::getCount)));
List<MonthlyTransaction> finalStat = new ArrayList<>();
//iterate over the total count map
totalMap.entrySet().stream().forEach(entry -> {
//Using the Stream filter to mimic a group by
MonthlyTransaction maxStat = transactions.stream()
.filter(t -> t.getMonth().equals(entry.getKey()))
//getting the item with the max count for the month
.max(Comparator.comparing(MonthlyTransaction::getCount)).get();
//Setting the count to the total value from the map as the max count value is not a requirement.
maxStat.setCount(entry.getValue());
//add the item to the list
finalStat.add(maxStat);
});
This may not be the best approach to the problem but this gives me the exact result. Thanks to everyone who had a look at it and tried to help.

Stream query with two lists

I have a list of Visit objects, now I want to build another list containing available hours for given day;
public class Visit {
private int id;
private Date date;
private Time time;
private Pet pet;
private Vet vet;
this is the array String[] containing all visit hours:
public class VisitTime {
private static final String[] visitTime =
{"09:00:00","09:30:00","10:00:00","10:30:00","11:00:00","11:30:00","12:00:00",
"12:30:00","13:00:00","13:30:00","14:00:00","14:30:00","15:00:00","15:30:00","16:00:00","16:30:00"};
so now Im getting from Db list of visits (each visit has defined time), and checking if there is any other free time to schedule a visit.
I have written two methods to do so, one with iteration second with streams, both working as expected.
What I'm asking is how can I rebuild this method to NOT use terminal method twice.
public List<String> getHoursAvailable12(int vetId, String date){
List<Visit> visitList = getVisitByVetIdAndDate(vetId, date);
List<String> hoursAvailable = new ArrayList<>(Arrays.asList(VisitTime.getVisittime()));
List<String> hoursTaken = visitList.stream().map(Visit::getTime).map(Time::toString).collect(Collectors.toList());
return hoursAvailable.stream().filter(x -> !hoursTaken.contains(x)).collect(Collectors.toList());
}
and here is old-school method with collections:
public List<String> getHoursAvailable(int vetId, String date){
List<Visit> visitList = getVisitByVetIdAndDate(vetId,date);
ArrayList<String> hoursAvailable = new ArrayList<>(Arrays.asList(VisitTime.getVisittime()));
for(Visit x : visitList){
{
String time = x.getTime().toString();
if(hoursAvailable.contains(time)) hoursAvailable.remove(time);
}
}
return hoursAvailable;
}
You can try this. You get some benefit here, contains is faster in HashSet compared to List
public Set<String> getHoursAvailable(int vetId, String date){
List<Visit> visitList = getVisitByVetIdAndDate(vetId,date);
Set<String> hoursAvailable = new LinkedHashSet<>(
Arrays.asList(VisitTime.getVisittime()));
visitList.stream()
.map(Visit::getTime)
.map(Time::toString)
.forEach(vt-> hoursAvailable.removeIf(s->s.equals(vt)));
return hoursAvailable;
}

How to create a summary of a list of objects?

I think it's a pretty basic question and there should be solutions out there, but I didn't manage to find any. I think I need more help in terminology, on what term I should look up to learn about my problem, but I really appreciate any help.
Anyway, I would like to implement the following:
I have a list of Objects. Each object is in the form of ExampleClass below:
public class ExampleClass {
private String name;
private Double firstDouble;
private Double secondDouble;
+ constructor and a bunch of methods
}
Basically I have a name variable and a bunch of numbers associated with each instance of ExampleClass. The name variable is not an id, so there may be several ExampleClasses in the list with the same name, all with different numbers associated with them. What I would like to do is to create a "summary" from this list:
Filtering out each instance of ExampleClass with the same name, so in my final list of objects, I do not have two objects with the same name variable.
I want to make operations with the Double variables of the objects with the same name.
So lets imagine I have the following ExampleClasses in my list:
ExampleClass first = new ExampleClass("apple",1,4);
ExampleClass second = new ExampleClass("pear",6,12);
ExampleClass third = new ExampleClass("apple",5,2);
ExampleClass fourth = new ExampleClass("peach",1,2);
ExampleClass fifth = new ExampleClass("plum",10,25);
In this case I want to remove from the list the first or third element, since they have the same name and I want to make an operation with the numbers, like adding up 1 and 5 and multiplying 4 and 2.
Thanks for any help in advance!
EDIT: I can solve it with a bunch of loops. However, I need my code to be readable and as efficient as it can get. I'm not looking for a brute force solution with nested loops, I'm interested if there is a nice, or nicer solution.
Finding the sum of all X values grouped by name is pretty easy in Java 8:
// Find the sum of X and group by name
Map<String, Integer> sumXByName = Stream.of(first, second, third, fourth, fifth)
.collect(groupingBy(ExampleClass::getName,
Collectors.<ExampleClass>summingInt(e -> e.getX())));
// Print the results
sumXByName.entrySet().stream()
.map(e -> e.getKey() + " -> " + e.getValue())
.forEach(System.out::println);
Prints:
plum -> 10
apple -> 6
pear -> 6
peach -> 1
However, finding the sum of X and product of Y requires a custom collector.
static class ExampleStatistics implements Consumer<ExampleClass> {
private Integer sum = 0;
private Integer product = null;
#Override
public void accept(ExampleClass value) {
sum += value.getX();
if (product == null) {
product = value.getY();
} else {
product *= value.getY();
}
}
public ExampleStatistics combine(ExampleStatistics other) {
sum += other.sum;
product *= other.product;
return this;
}
public Integer getSum() {
return sum;
}
public Integer getProduct() {
return product;
}
#Override
public String toString() {
return String.format("Sum X = %d, Product Y = %d", sum, product);
}
}
static class ExampleSummarizer
implements Collector<ExampleClass, ExampleStatistics, ExampleStatistics> {
#Override
public Supplier<ExampleStatistics> supplier() {
return ExampleStatistics::new;
}
#Override
public BiConsumer<ExampleStatistics, ExampleClass> accumulator() {
return (r, t) -> r.accept(t);
}
#Override
public BinaryOperator<ExampleStatistics> combiner() {
return (r, t) -> r.combine(t);
}
#Override
public Function<ExampleStatistics, ExampleStatistics> finisher() {
return i -> i; // identity finish
}
#Override
public Set<Collector.Characteristics> characteristics() {
return Stream.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED)
.collect(toSet());
}
};
Now you can easily summarize the objects:
// Summarize all examples and group by name
Map<String, ExampleStatistics> statsByName = Stream.of(first, second, third, fourth, fifth)
.collect(groupingBy(ExampleClass::getName, new ExampleSummarizer()));
Printing this map will yield the following:
plum -> Sum X = 10, Product Y = 25
apple -> Sum X = 6, Product Y = 8
pear -> Sum X = 6, Product Y = 12
peach -> Sum X = 1, Product Y = 2
EDIT: I used integers for convenience. However, there are summarizing equivalents available for doubles e.g. summingDouble.
class ExampleClass {
private final String name;
private final Integer x;
private final Integer y;
public ExampleClass(String name, Integer x, Integer y) {
super();
this.name = name;
this.x = x;
this.y = y;
}
public String getName() {
return name;
}
public Integer getX() {
return x;
}
public Integer getY() {
return y;
}
}
Filtering out each instance of ExampleClass with the same name, so in my final list of objects, I do not have two objects with the same name variable.
Consider making your class implement equals(Object obj) and Comparable interface or provides some means of creating a Comparator object.
Implement a SortedSet collection for your list such as TreeSet
I want to make operations with the Double variables of the objects
with the same name.
You could use the contains(Object obj) method provided by any Set inherited collection before adding an item to your list so that if the method returns true, you could do something with the duplicate. It is recommended that you keep your equals implementation consistent with your Comparable implementation.
Instead of
ExampleClass first = new ExampleClass("apple",1,4);
ExampleClass second = new ExampleClass("pear",6,12);
ExampleClass third = new ExampleClass("apple",5,2);
ExampleClass fourth = new ExampleClass("peach",1,2);
ExampleClass fifth = new ExampleClass("plum",10,25);
use List
List<ExampleClass> list = new ArrayList<>(5);
list.add(new ExampleClass("apple",1,4));
...
delete the first or third element
list.remove(1);
list.remove(3);
Other you can do by yourself, I think. You should just read about List, Map, Set and so on.

Categories