I ran into following situation and I am wondering about best solution. Let's say I have List<Object1> and List<Object2>, these lists are result of two separated queries. Both of them have the same size and there is relationship 1:1 between elements in the lists based on ID. I know that best solution would be fetching data in one DB query but that's possible right now.
So my question is, what is the best way to join these lists into let's say List<Object3>?
I would
convert the first list into Map with key as the ID and value as Object1.
iterate through the second list, get the object1 corresponding to the ID of object2.
perform the merge operation or whatever is intended and prepare object3 and put it in a list.
The resultant order would be that of in second list.
Make sure the queries order by id and then just iterate over the lists at the same time, whilst creating your new super list. This isn't foolproof, if the queries return differing sets of ids then the data will be corrupt. You could always add a check to make sure the ids match before adding the two objects to the super list though.
Use Apache Common BeanUtils as you don't have to write much code, also datatype convertions will be proper.
In this case both object1List and object2List should be in same order based on ID. To have that use Comparable interface to sort these based on ID for both Object1 and Object2
Example Code
Object1.java
public class Object1 implements Comparable<Object1>{
private Integer id;
private String name;
private int quantity;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int compareTo(Object1 compareObject1) {
int compareId = ((Object1) compareObject1).getId();
//ascending order
return this.id - compareId;
}
}
Object2.java
public class Object2 implements Comparable<Object2>{
private Integer id;
private double amount ;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public int compareTo(Object2 compareObject2) {
int compareId = ((Object2) compareObject2).getId();
//ascending order
return this.id - compareId;
}
}
Object3.java which will have fields names same as Object1 and Object2.java
Actual Implementation
int cnt = 0;
List<Object3> object3List = new ArrayList<Object3>();
List<Object1> object1List = Collections.sort(object1List);
List<Object2> object2List = Collections.sort(object2List);
for(Object1 obj1 : object1List) {
BeanUtils.copyProperties(obj3, obj1);
Object2 obj2 = object2List.get(cnt);
BeanUtils.copyProperties(obj3, obj2);
object3List.add(obj3);
cnt ++;
}
I would use a BidiMap (see Apache Commons Collections).
Moreover, if you're sure the two lists have the same size and each id is present within the two lists, you could sort the list by the id and go through them with a classic for loop.
Comparator<YourObject> comparator = new Comparator<YourObject>() {
#Override
public int compare(YourObject o1, YourObject o2) {
return o1.getId().compareTo(o2.getId());
}
};
Collections.sort(list1, comparator);
Collections.sort(list2, comparator);
// import org.apache.commons.collections.BidiMap;
// import org.apache.commons.collections.bidimap.DualHashBidiMap;
BidiMap map = new DualHashBidiMap();
// required: list1.size() == list2.size()
for (int i = 0; i < list1.size(); i++) {
map.put(list1.get(i), list2.get(i));
}
If equals and hashCode are using the ID, and they should if this represents the uniqueness in your objects, you could also use sets instead of lists. This way you will be able to do something like set1.addAll(set2)
Related
I have a set of objects of type "Part" and each Part is associated with quantity(a field that specifies number of units required of that particular Part ). Is there in way in java to store this data, other than hash map?
You can write a wrapper object that has two fields, Part and quantity:
public class PartWithQuantity
{
private Part part;
private int quantity;
public int getQuantity() { return quantity; }
public void setQuantity(int q) { quantity = q; }
public Part getPart() { return part; }
public void setPart(Part p) { part = p; }
}
You can also use a TreeMap if the concern is that the HashMap might use too much memory.
This can be done very easily by just storing the metadata into the arraylist.
You can take a class that contains one reference of part type and other(quantity) is int type. Then you can even store the instances of this new class into an arraylist. Do Remember ,you can access your information anytime from this arraylist by calling the object.
The simplest way to achieve this would be to modify the Part class and add the quantity field, along with its setter and getter.
Another option would be to use a wrapper object and store it in the Set.
I have created a convenience method called check() to easily identify the Part of interest:
public class PartExtended {
private Part part;
private int quantity;
public PartExtended(Part part, int quantity) {
this.part = part;
this.quantity = quantity;
}
public boolean check(Part part) {
return part.equals(this.part);
}
public Part getPart() {
return part;
}
public int getQuantity() {
return quantity;
}
}
To retrieve a Part's quantity, use a for loop:
Set<PartExtended> data = new HashSet<>();
data.add(new PartExtended(new Part(), 0));
for (PartExtended item : data) {
if (item.check(new Part())) {
int quantity = item.getQuantity();
}
}
The answer to this will probably turn out to be obvious in retrospect but for now I find myself rather stuck on this. I'll give some blocks of code first and then present the problem.
This is part of my class Stockmanager, I have omitted some methods that have nothing to do with this problem.
import java.util.ArrayList;
public class StockManager
{
private ArrayList stock;
public StockManager()
{
stock = new ArrayList();
}
public void addProduct(Product item)
{
stock.add(item);
}
public Product findProduct(int id)
{
int index = 0;
while (index < stock.size())
{
Product test = stock.get(index);
if (test.getID() == id)
{
return stock.get(index);
}
index++;
}
return null;
}
public void printProductDetails()
{
int index = 0;
while (index < stock.size())
{
System.out.println(stock.get(index).toString());
index++;
}
}
}
Here's my class Product, again with some methods omitted.
public class Product
{
private int id;
private String name;
private int quantity;
public Product(int id, String name)
{
this.id = id;
this.name = name;
quantity = 0;
}
public int getID()
{
return id;
}
public String getName()
{
return name;
}
public int getQuantity()
{
return quantity;
}
public String toString()
{
return id + ": " +
name +
" voorraad: " + quantity;
}
}
My problem lies in the fact that I get a compile time error in the findProduct() method. To be more specific the line Product test = stock.get(index); is indicated with a message incompatible types.
The constructor of StockManager creates a new ArrayList with the name stock. As is evident from the method addProduct() this ArrayList contains items of the type Product. The Product class has a number of variables one of which is called id and is of type integer. That class also contains a method getID() that returns an id.
As far as I know, the way of getting an item from an arraylist is the get() method with the number between the () indicating the item's position. Seeing as my arraylist contains instances of Product, I expect to get a Product as result when I use the get() method on the arraylist. So I don't understand why it doesn't work when I define a variable called test of the type Product and try to assign an item from the arraylist to it. I have as far as I know, successfully used this same technique in the method printProductDetails() where I use the toString() method from Product on the object from the arraylist.
I hope someone will be able to clarify for me where I am at fault. If it makes any difference, I am doing this stuff in BlueJ which is probably not the best tool for it but it is the one I'm supposed to use for this school project.
private ArrayList stock;
You should redeclare this with a bounded type like so:
private List<Product> stock = new ArrayList<Product>();
If you don't, this line:
Product test = stock.get(index);
won't work because you're trying to assign a raw Object to a Product.
Others have suggested casting the Object to a Product, but I wouldn't recommend this.
Your stock is defined as private ArrayList stock, which means that stock.get() returns an Object without any special type. You should either make Stock an ArrayList of Products
ArrayList<Product> stock;
or cast the result of the get method manually
Product test = (Product)stock.get(whatever);
Product test = (Product) stock.get(index);
or if you make your list List<Product> stock your line should work without changes.
I have a list of java beans, now I want to sort them with specified property and sort order
(the property and sort order are input parameters), like this:
class Person{
private String userName;
private Integer age;
private String address;
public void sort(List<Person> ps, String property, String sortOrder)
{
// How to use the property and sortOrder??
Collections.sort(ps);
}
}
What is the best way of writing the sortList() method?
actually I have one way to do this. I can write two static properties for Person. then I set these two properties before sorting:
class Person implements Comparable<Person>{
private String userName;
private Integer age;
private String address;
public static String sortProperty;
public static String sortOrder;
public void sort(List<Person> ps, String property, String sortOrder)
{
Person.sortProperty=property;
Person.sortOrder=sortOrder;
Collections.sort(ps);
}
#Override
public int compareTo(Person o)
{
// find the property with Person.sortProperty using reflection
// then sort the property
}
}
This is not a good solution. Could anyone give me some suggestion? thanks in advance
Hope this makes sense for you. Use final keyword to avoid creating static variable for passing sort order. Also changed it to boolean for easy access. Below code is just for illustration.
public static void sort(List<Person> ps, String property, final boolean asc) {
if (property.equals("userName")) {
Collections.sort(ps, new Comparator<Person>() {
public int compare(Person o1, Person o2) {
// pls use appropriate compareTo I always get confused
// which one to call when it is asc or desc.
if (asc)
return o1.getUserName().compareTo(o2.getUserName());
else
return o2.getUserName().compareTo(o1.getUserName());
}
});
}
if (property.equals("age")) {
Collections.sort(ps, new Comparator<Person>() {
public int compare(Person o1, Person o2) {
if (asc)
return o1.getAge().compareTo(o2.getAge());
else
return o2.getAge().compareTo(o1.getAge());
}
});
}
}
You can implement a Comparator and use it like this.
Collections.sort(ps, myComparator)
Inside the comparator you can just retrieve property and sortOrder from person and compare with these variable
Collections.sort(ps, new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
int result = 0;
if(sortProperty.equals("userName")) {
result = p1.userName.compareTo(p2.userName);
} else if(//...
//determine how based on sortOrder here
//e.g.
if(sortOrder.equals("ascending") {
return result;
} else {
return (result * (-1));
}
}
});
Note: property and sortOrder need to be declared final.
You should be able to use the Bean Comparator for this.
Write three Comparators. They could be static fields. E.g.
public static final Comparator COMPARE_BY_NAME = (your code here)
And similar for COMPARE_BY_AGE etc.
Pass them to the standard Java sort() methods.
To make the sorting as generic extended method of a list you can use below code:
public static List<T> SortList<T>(this List<T> list, string sortDirection, string sortExpression)
{
if (sortDirection.ToLower() == "sorting_asc")
{
return (from n in list
orderby GetDynamicSortProperty(n, sortExpression) ascending
select n
).ToList();
}
else if (sortDirection.ToLower() == "sorting_desc")
{
return (from n in list
orderby GetDynamicSortProperty(n, sortExpression) descending
select n
).ToList();
}
else
{
return list;
}
}
public static object GetDynamicSortProperty(object item, string propName)
{
//Use reflection to get order type
return item.GetType().GetProperty(propName).GetValue(item, null);
}
Calling:
List<Employee> employees = new List<Employee>();
var sortedEmployees = employees.SortList(sortedorder, sortedcolumn);
i have a java class like this
public class A {
private String field1;
private String field2;
// getters, setters but no equals and hashcode
}
and a list of objects of this class, i want to remove from this list all the duplicates elements that has the same field1 or the same field2, so i have 2 Comparators
public class Comparator1 implements Comparator<A> {
public int compare(A o1, A o2) {
return o1.getField1().compareToIgnoreCase( o2.getField1() );
}
}
public class Comparator2 implements Comparator<A> {
public int compare(A o1, A o2) {
return o1.getField2().compareToIgnoreCase(o2.getField2());
}
}
so to do the task i use treeset like
TreeSet<A> ts1 = new TreeSet<A>(new Comparator1())
ts1.addAll(list)
TreeSet<A> ts2 = new TreeSet<A>(new Comparator2())
ts2.addAll(ts1)
list.clear()
list.addAll(ts2)
but how can i do the same using just one comparator and one treeset ?
Thanks for the help
Update:
Thanks all for the answers, but after reading them i don't know if this is the right approach to the real problem.
In my real case field1 is like a phone number and field2 is like a name.
So i don't want to call the same phone number more than one time (this is the first treeset to removes duplicates) and i don't want to call more than one time the same name (the second treeset to removes duplicates)
You can modify the class but i'd like to know if this approach is ok to resolve the real problem.
If this approach is correct, from your question, i see that without modifying the class is not possible to use just one comparator
Thanks
You can't use one comparator to sort by two criteria at the same time, so there is no real way to go better than two TreeSets in your case. Of course, you can wrap them in one data structure.
(Alternatively you could use two HashMaps, each having one of the strings as key - this will be faster on average, but is more complicated to program.)
You can't, and it's not clear to me that what you're trying to do is well-defined.
Are you aware that your current approach depends both on the order in which elements are added and on whether you check field1 or field2 first for duplicates? Imagine you had these objects of class A:
A ab = new A("a", "b");
A cb = new A("c", "b");
A cd = new A("c", "d");
Checking field1 first gives the result [ab] or [ab, cd], depending on the order added.
Checking field2 first gives the result [cb] or [ab, cd], depending on the order added.
This is pretty strange behavior. Is this what you intended? I don't think it is possible to reproduce this with a single TreeSet and Comparator in the general case.
public static <A extends Comparable<?>> TreeSet<A> getTreeSet(Collection<A> list){
TreeSet<A> result = new TreeSet<A>();
HashSet<A> unique = new HashSet<A>();
unique.addAll(list);
result.addAll(unique);
return result;
}
Generic function that adds items to hashset to make them unique, and then drop them to TreeSet to sort. You can use it with: TreeSet<A> ts1 = getTreeSet(list);.
This approach works well for a fixed list.
#BalusC No, this assumes
public class A implements Comparable<A> {
private String field1;
private String field2;
#Override
public int compareTo(A o) {
// No null checks, because it's illegal anyways.
int tmp = 0;
if ((tmp = field1.compareToIgnoreCase(o.field1)) != 0)
return tmp;
if ((tmp = field2.compareToIgnoreCase(o.field2)) != 0)
return tmp;
return tmp;
}
// getters, setters but no equals and hashcode
}
If your intention is to do two levels of sorting(first: PhoneNumber and second:Name), then you can use the following code, where the duplicate check will be done against both the fields(field1 and field2). As we are already using compareTo for both the fields, it is not required to use equals and hashcode. But it is always good practice to use hashcode and equals.
public class A implements Comparable<A> {
private String field1;
private String field2;
public A(String number, String name) {
this.field1 = number;
this.field2 = name;
}
// First level sorting will be done by field1.
// If field1 is equal then second level sorting will be done on field2
#Override
public int compareTo(A o) {
int compareTo = field1.compareTo(o.getNumber());
if(compareTo==0){
return field2.compareTo(o.getName());
}
return compareTo;
}
public String getNumber() {
return field1;
}
public String getName() {
return field2;
}
}
public class RemoveDuplicate {
public static void main(String[] args) {
final ArrayList<Student> students = new ArrayList<Student>();
Set<Student> set = new TreeSet<Student>();
Student[] starr = new Student[6];
starr[0] = new Student("Student1", "1005");
starr[1] = new Student("Student2", "1004");
starr[2] = new Student("Student3", "1003");
starr[3] = new Student("Student6", "1002");
starr[4] = new Student("Student5", "1001");
starr[5] = new Student("Student6", "1000");
Arrays.sort(starr, Student.StudentIdComparator);
for (Student s : starr) {
students.add(s);
}
System.out.println(students);
set.addAll(students);
System.out.println("\n***** After removing duplicates *******\n");
final ArrayList<Student> newList = new ArrayList<Student>(set);
/** Printing original list **/
System.out.println(newList);
}}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.List;
public class RemoveDuplicate {
public static void main(String[] args) {
Set<Student> set = new TreeSet<Student>();
List<Student> students = Arrays.asList(new Student("Student1", "1005"), new Student("Student2", "1004"),
new Student("Student3", "1003"), new Student("Student6", "1002"), new Student("Student5", "1001"),
new Student("Student6", "1000"));
// Sorting Using Lambda
students.sort(new Comparator<Student>() {
#Override
public int compare(Student s1, Student s2) {
return s1.getId().compareTo(s2.getId());
}
});
System.out.println(students);
set.addAll(students);
System.out.println("\n***** After removing duplicates *******\n");
final ArrayList<Student> newList = new ArrayList<Student>(set);
/** Printing original list **/
System.out.println(newList);
}
}
class Student implements Comparable<Student> {
private String name;
private String id;
public Student(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public String toString() {
return "\n" + "Name=" + name + " Id=" + id;
}
#Override
public int compareTo(Student o1) {
if (o1.getName().equalsIgnoreCase(this.name)) {
return 0;
}
return 1;
}
// public static Comparator<Student> StudentIdComparator = (Student
// s1,Student s2) -> s1.getId().compareTo(s2.getId());
}
I am storing objects in ArrayList, where my pojo is as
public class POJOSortableContacts {
private Long id;
private String displayName;
public POJOSortableContacts(Long id, String displayName) {
super();
this.id = id;
this.displayName = displayName;
}
//Setter and Getters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
//This will be used to sectioned header.
public String getLabel() {
return Character.toString(displayName.charAt(0)).toUpperCase();
}
//Sortable categories
//Sort by Contact name
public static Comparator<POJOSortableContacts> COMPARE_BY_NAME = new Comparator<POJOSortableContacts>() {
public int compare(POJOSortableContacts one, POJOSortableContacts other) {
return one.getDisplayName().compareToIgnoreCase(other.getDisplayName());
//return s1.toLowerCase().compareTo(s2.toLowerCase()); //it returns lower_case word first and then upper_case
}
};
//Sort by id
public static Comparator<POJOSortableContacts> COMPARE_BY_ID = new Comparator<POJOSortableContacts>() {
public int compare(POJOSortableContacts one, POJOSortableContacts other) {
return one.id.compareTo(other.id);
}
};
}
and Arraylist structure is as
ArrayList<POJOSortableContacts> contactArrayList = new ArrayList<POJOSortableContacts>()
, I want to search an object from contactArrayList by id (for example I want an object which id is 20), I want to use binarysearch for this. So how can it will be?
You can use
POJOSortableContacts contact = Collections.binarySearch(contactArrayList,
new POJOSortableContacts(20, ""),
COMPARE_BY_ID);
The new POJOSortableContacts is just a dummy object to act as the key.
Of course, this will only work if your list is sorted by ID to start with - you can't use a binary search on an unsorted list (or on a list which is sorted in a different way).
I will rather suggest that you use a HashMap.
Map<Long,POJOSortableContacts> contactMap = new HashMap<Long,POJOSortableContacts>();
Fill up your contactMap like this:
contactMap.put(myContact.getId(), myContact);
Searching then becomes trivial:
POJOSortableContacts myContact = contactMap.get(myID);
To be able to use binary search, your collection must be sorted. You could sort your ArrayList each time before your search, but that would negate the advantage of using binary search (you could just do a linear search over the unsorted list and still be faster).
ArrayList has a method - BinarySearch, which takes object to search as a parameter.
POJOSortableContacts contactToSearch = new POJOSortableContacts(someId, "Name");
POJOSortableContacts myContact = contactArrayList.BinarySearch(contactToSearch);
Hope this helps.
Sidestepping the question a bit, if you can't have duplicates in the list you'd likely be better served by using a SortedSet to store the contacts. No sorting before using binarySearch anymore...