Manipulating data in java List<Object> in a structured way - java

I have the following class :
class Students{
int age;
int dept;
}
Lets say i have a List<Students> and I want to manipulate the list by doing simple calculations like : calculate the mean, calculate the middle value (e.g. (age+debt)/2), find the closest value to the mean and so on. How can I do this in a structured way?. I want to be in a position where I can use different combinations on the list. e.g. calculate mean of age // calculate mean of the middle value from age/debt, find the closest value of the age etc.
How should i approach this?. Would appreciate it if someone could point me in the right direction.

Apache Math has a nice Descriptive Statistics package that does this sought of thing.
http://commons.apache.org/proper/commons-math/userguide/stat.html#a1.2_Descriptive_statistics
If you're using Java 8 this works well with Lambdas:
DescriptiveStatistics stats = new DescriptiveStatistics();
students.forEach(s -> stats.add(s.age));
double mean = stats.getMean();
And to filter etc:
//Only students with an age > 18
students.stream.filter(s -> s.age > 18).forEach(s -> stats.add(s.age));
If you're not using Java 8 then simply foreach it.

You can create a separate class (StudentCalculator) that will require a List of Students (perhaps pass the List in the constructor) and have the instance methods perform calculations on the List.
Or you can create a utility (e.g. StudentCalculatorUtility) where you would define a series of methods that would accept a List of Students as a parameter, that would perform all the calculations you would need on the students(middle value,closest to mean, etc.)

There is a concept where you step through a list and perform an operation on each item in turn which may or may not change the item.
In this case, you want a method that takes an item from the list does some stuff and returns a running total.
int sumItems(Student stu, int sum){
return (stu.age + stu.debt)/2;
}
To use this method, either use either a forEach or an iterator.
Iterator itr = Students.iterator(); // assuming List<Student> Students = new List<Student>()
int sum = 0;
while(itr.hasnext()){
sum = sumItems(itr.next(), sum)
}
Now do something with your sum.

Related

List of 10 Taxpayers who spent the most

I need to return a List, or a Collection in general, that gives me the 10 taxpayers who spent the most in the entire system. The classes are divided in User, Taxpayer (which extends User) and Expense, and in my main class Main I have a Map holding every single value for Users and Expenses, respectively a Map<String, User> users and a Map<String, Expense> expenses.
The first step would be to go through the Map of users and check if it's a Taxpayer , then for that Taxpayer get all the Expenses he has done. Inside each expense there's a variable called Value with a getValue method to return the Value.
I've tried to do it but I was having a problem in updating the Collection if the next Taxpayer had a higher sum on Expense values than the one on the "end" of the Collection.
Also, I would prefer if this wasn't done in Java 8 since I'm not very comfortable with it and there's more conditions that I would need to set in the middle of the method.
Edit (what I have until now):
public List<Taxpayer> getTenTaxpayers(){
List<taxpayer> list = new ArrayList<Taxpayer>();
for(User u: this.users.values()){
if(!u.getUserType()){ // if it is a Taxpayer
Taxpayer t = (Taxpayer) u;
double sum = 0;
for(Expense e: this.expenses.values()){
if(t.getNIF().equals(e.getNIFClient())){ //NIF is the code that corresponds to the Taxpayer. If the expense belongs to this Taxpayer, enters the if statement.
sum += e.getValue();
if(list.size()<10){
list.add(t.clone());
}
}
}
}
}
}
So if I understand correctly, when you already have 10 Taxpayers in your list, you are struggling on how to then add another taxpayer to the list to maintain a only to top 10 "spenders"
One way to approach this is to gather the expenses of all your Taxpayers and add them all to your list. Then sort the list in reverse order by the amount they have spent. Then just get the first 10 entries from the list.
You could do this using the Collections.sort() method defining your own custom Comparator
Something like:
List<Taxpayer> taxpayers =...
Collections.sort(taxpayers, new Comparator<Taxpayer>()
{
#Override
public int compare(Taxpayer o1, Taxpayer o2)
{
return o1.sum - o2.sum; // using your correct total spent here
// or to just sort in reverse order
// return o2.sum - o1.sum;
}
});
Or if Taxpayer implements Comparable you can just use
Collections.sort(taxpayers)
Then reverse
Collections.reverse(taxpayers)
Then get top 10
List<Taxpayer> top10 = taxpayers.subList(0, 10);
To be more efficient though you could just define the comparator to sort the list in reverse order - then you don't need to reverse the list - just get the top 10.

Iterate an ArrayList to count by object attribute

I have an ArrayList of products already initialized. The Product constructor is:
public Product(String number, String type, double rentalDeposit, double rentalPrice, double lateFee, double buyPrice, int maxDuration)
The type is determined by an enumeration:
protected enum productType {Basket, BabySeat, Helmet, Headlight, Bell};
I pass in the type using the toString method for the enumeration. I need to iterate through the ArrayList<Product> (given by shop.getInventory()) that I have and count how many of each type there are, i.e. how many are of type Basket, BabySeat, Helmet, etc.
The Product class has a getType() method that returns a string.
for (Product.productType product : Product.productType.values()) {
int occurences = Collections.frequency(shop.getInventory(), product.toString());
}
I have tried using Collections.frequency, but it keeps returning 0 and I'm not sure why.
Is there another way to iterate through and find this amount without using a ton of if statements?
shop.getInventory() I'll assume has the type Collection<Product>. You can either define product such that .equals(Product) will check equality against the Product's internal type, or even more simply, shop.getInventory().stream().filter(item -> product.toString().equals(item.getType())).count(). (replace item.getType() with however you extract the type field from Product, like maybe item.type etc).
A simple method of counting items in a list that correspond to some condition is to use Collectors.groupingBy and Collectors.counting. Something like the following:
Map<ProductType,Long> counts = products.stream()
.collect(groupingBy(Product::getType, counting()));
If you're not familiar with streams, this statement can be read as 'turn the list into a stream of products, group the products by product type then count each of those groups creating a map from the type to the count.'

java: Collect and combine data in a list

In my program I have a List of Plants, each plant has a measurement (String), day (int), camera (int), and replicate number(int). I obtain a List of all plants wanted by using filters:
List<Plant> selectPlants = allPlants.stream().filter(plant -> passesFilters(plant, filters)).collect(Collectors.toList());
What I would like to do now is take all Plants that have the same camera, measurement, and replicate values. And combine them in order of day. So if I have days 1,2,3,5 I would want to find all similar plants and append the values to one plant where the getValues (function).
I added a method to Plant that appends values by just using addAll( new plant values ).
Is there any way of doing this without iterating through the list over and over to find the similar plants, and then sorting each time by day then appending? I'm sorry for the horrible wording of this question.
While Vakh’s answer is correct, it is unnecessarily complex.
Often, the work of implementing your own key class does not pay off. You can use a List as a key which implies a slight overhead due to boxing primitive values but given the fact that we do operations like hashing here, it will be negligible.
And sorting doesn’t have to be done by using a for loop and, even worse, an anonymous inner class for the Comparator. Why do we have streams and lambdas? You can implement the Comparator using a lambda like (p1,p2) -> p1.getDay()-p2.getDay() or, even better, Comparator.comparing(Plant::getDay).
Further, you can do the entire operation in one step. The sort step will create an ordered stream and the collector will maintain the encounter order, so you can use one stream to sort and group:
Map<List<?>, List<Plant>> groupedPlants = allPlants.stream()
.filter(plant -> passesFilters(plant, filters))
.sorted(Comparator.comparing(Plant::getDay))
.collect(Collectors.groupingBy(p ->
Arrays.asList(p.getMeasurement(), p.getCamera(), p.getReplicateNumber())));
That’s all.
Using Collectors.groupBy:
private static class PlantKey {
private String measurement;
private int camera;
private int replicateNumber;
// + constructor, getters, setters and haschode equals
}
Map<PlantKey, List<Plant>> groupedPlants =
allPlants.stream().filter(plant -> passesFilters(plant, filters))
.collect(Collectors.groupBy(p ->
new PlantKey(p.getMeasurement(),
p.getCamera(),
p.getReplicateNumber())));
// order the list
for(List values : groupedPlants.values()) {
Collections.sort(values, new Comparator<Plant>(){
#Override
public int compare(Plant p1, Plant p2) {
return p1.getDay() - p2.getDay();
}
});
}
I would group them by the common characteristics and compare similar results.
for(List<Plant> plantGroup : allPlants.stream().collect(Collectors.groupingBy(
p -> p.camera+'/'+p.measurement+'/'+p.replicate)).values()) {
// compare the plants in the same group
}
There is a function called sorted which operates on a stream
selectPlants.stream().sorted(Comparator.comparingInt(i -> i.day)).collect(Collectors.toList());

Create a sorted by value Map before putting any data in it

I know answer to this question has been provided in many variants but I couldn't find it for my specific query.
I want to have a map which is sorted by values and I need to get it created before I put data into it. I came up with below code to create it
private Map<String, Integer> mapUserScore = new ConcurrentSkipListMap<>(new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
int i1=mapUserScore.get(o2);
int i2=mapUserScore.get(o1);
if(mapUserScore.get(o2)!=null && mapUserScore.get(o1)!=null){
int compare = mapUserScore.get(o2)-(mapUserScore.get(o1));
if(compare==0)compare=-1;
return compare;
}else
return 0;
}
});
So basically I want entries in map sorted by integer values in descending order so that highest scorers are on top.
However upon doing this when the first key-value pair is inserted, the program exits with below exception
Exception in thread "Thread-0" java.lang.StackOverflowError
at java.util.concurrent.ConcurrentSkipListMap.comparable(ConcurrentSkipListMap.java:658)
at java.util.concurrent.ConcurrentSkipListMap.doGet(ConcurrentSkipListMap.java:821)
at java.util.concurrent.ConcurrentSkipListMap.get(ConcurrentSkipListMap.java:1626)
Upon tracing, I found that line int i1=mapUserScore.get(o2) results in this exception.
Can anyone please help me to understand what could be the reason of stackoverflow here?
I am thinking that because before any item is stored in the map, code is trying to obtain it by using get() method to sort it and hence it goes into some recursive calls and results in exception.
If I understand correctly, you would like to be able to get the score associated to a name quickly (hence the need for a Map), and you would like to be able to iterate through tyhe name-score pairs with the highest scores first.
I would just use a HashMap<String, NameScore> (where the key is the name and the value is the name-score pair). This would give you O(1) lookups. And when you need to name-score pairs sorted by score, create a new ArrayList<NameScore> from the values() of the map, sort it, and return it.
The get() method uses the comparator to find the value. You can't use get in the comparator or your will get a stack over flow.
A simple work around is to include the score in the key and sort on that instead.
class NameScore implement Comparable<NameScore> {
String name;
int score;
}
BTW: When the comparator return 0 this means it is duplicate which is dropped. Unless you want only one name per score, you need to compare on the score AND the name.

search object within java collection

first, I don't know if I'm organizing my data efficiently, the idea is that I have pairs of key/value.
public static class Freq implements Comparable {
String term;
double frequency;
public Freq( String term, double frequency ) {
this.term = term;
this.frequency = frequency;
}
public int compareTo(Object o) {
if(this.frequency == ((Freq) o).frequency)
return 0;
else if(this.frequency > ((Freq) o).frequency)
return 1;
else
return -1;
}
Now, I'm storing such objects within a collection: List<Freq> bla = new ArrayList<Freq>() as well as sorting it.
I'm interested to search for specific objects e.g. Freq.name = 'Bar' from the collection, which is sorted. How would I do that? Or I have to iterate the whole collection.
or is there other more efficient ways to do this?
You should use an associated collection such as a TreeMap, which keeps its elements sorted automatically. If you want to search sometimes based on name and sometimes on frequency, you can keep your elements in two maps at the same time, and use the suitable one for lookup.
Or if for some reason you want to stick with a sorted List, you can use Collections.binarySearch() to find elements in it.
You can use JFilter http://code.google.com/p/jfilter/
JFilter is a simple and high performance open source library to query collection of Java beans.
Key features
Support of collection (java.util.Collection, java.util.Map and Array) properties.
Support of collection inside collection of any depth.
Support of inner queries.
Support of parameterized queries.
Can filter 1 million records in few 100 ms.
Filter ( query) is given in simple json format, it is like Mangodb queries. Following are some examples.
{ "id":{"$le":"10"}
where object id property is less than equals to 10.
{ "id": {"$in":["0", "100"]}}
where object id property is 0 or 100.
{"lineItems":{"lineAmount":"1"}}
where lineItems collection property of parameterized type has lineAmount equals to 1.
{ "$and":[{"id": "0"}, {"billingAddress":{"city":"DEL"}}]}
where id property is 0 and billingAddress.city property is DEL.
{"lineItems":{"taxes":{ "key":{"code":"GST"}, "value":{"$gt": "1.01"}}}}
where lineItems collection property of parameterized type which has taxes map type property of parameteriszed type has code equals to GST value greater than 1.01.
{'$or':[{'code':'10'},{'skus': {'$and':[{'price':{'$in':['20', '40']}}, {'code':'RedApple'}]}}]}
Select all products where product code is 10 or sku price in 20 and 40 and sku code is "RedApple".

Categories