search object within java collection - java

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".

Related

Check if all object entities are equal using Java Streams [duplicate]

I am new to Java 8. I have a list of custom objects of type A, where A is like below:
class A {
int id;
String name;
}
I would like to determine if all the objects in that list have same name. I can do it by iterating over the list and capturing previous and current value of names. In that context, I found How to count number of custom objects in list which have same value for one of its attribute. But is there any better way to do the same in java 8 using stream?
You can map from A --> String , apply the distinct intermediate operation, utilise limit(2) to enable optimisation where possible and then check if count is less than or equal to 1 in which case all objects have the same name and if not then they do not all have the same name.
boolean result = myList.stream()
.map(A::getName)
.distinct()
.limit(2)
.count() <= 1;
With the example shown above, we leverage the limit(2) operation so that we stop as soon as we find two distinct object names.
One way is to get the name of the first list and call allMatch and check against that.
String firstName = yourListOfAs.get(0).name;
boolean allSameName = yourListOfAs.stream().allMatch(x -> x.name.equals(firstName));
another way is to calculate count of distinct names using
boolean result = myList.stream().map(A::getName).distinct().count() == 1;
of course you need to add getter for 'name' field
One more option by using Partitioning. Partitioning is a special kind of grouping, in which the resultant map contains at most two different groups – one for true and one for false.
by this, You can get number of matching and not matching
String firstName = yourListOfAs.get(0).name;
Map<Boolean, List<Employee>> partitioned = employees.stream().collect(partitioningBy(e -> e.name==firstName));
Java 9 using takeWhile takewhile will take all the values until the predicate returns false. this is similar to break statement in while loop
String firstName = yourListOfAs.get(0).name;
List<Employee> filterList = employees.stream()
.takeWhile(e->firstName.equals(e.name)).collect(Collectors.toList());
if(filterList.size()==list.size())
{
//all objects have same values
}
Or use groupingBy then check entrySet size.
boolean b = list.stream()
.collect(Collectors.groupingBy(A::getName,
Collectors.toList())).entrySet().size() == 1;

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.'

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

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.

Sorting of 2 or more massive resultsets?

I need to be able to sort multiple intermediate result sets and enter them to a file in sorted order. Sort is based on a single column/key value. Each result set record will be list of values (like a record in a table)
The intermediate result sets are got by querying entirely different databases.
The intermediate result sets are already sorted based on some key(or column). They need to be combined and sorted again on the same key(or column) before writing it to a file.
Since these result sets can be massive(order of MBs) this cannot be done in memory.
My Solution broadly :
To use a hash and a random access file . Since the result sets are already sorted, when retrieving the result sets , I will store the sorted column values as keys in a hashmap.The value in the hashmap will be a address in the random access file where every record associated with that column value will be stored.
Any ideas ?
Have a pointer into every set, initially pointing to the first entry
Then choose the next result from the set, that offers the lowest entry
Write this entry to the file and increment the corresponding pointer
This approach has basically no overhead and time is O(n). (it's Merge-Sort, btw)
Edit
To clarify: It's the merge part of merge sort.
If you've got 2 pre-sorted result sets, you should be able to iterate them concurrently while writing the output file. You just need to compare the current row in each set:
Simple example (not ready for copy-and-paste use!):
ResultSet a,b;
//fetch a and b
a.first();
b.first();
while (!a.isAfterLast() || !b.isAfterLast()) {
Integer valueA = null;
Integer valueB = null;
if (a.isAfterLast()) {
writeToFile(b);
b.next();
}
else if (b.isAfterLast()) {
writeToFile(a);
a.next();
} else {
int valueA = a.getInt("SORT_PROPERTY");
int valueB = b.getInt("SORT_PROPERTY");
if (valueA < valueB) {
writeToFile(a);
a.next();
} else {
writeToFile(b);
b.next();
}
}
}
Sounds like you are looking for an implementation of the Balance Line algorithm.

Categories