I am having trouble solving a particular problem in Java (which I did not find by search). I do not know how to create a nested lists of objects - with a different type of object/primitive type at the end. For example:
*Note: only an example. I am actually doing this below with something other than Employee, but it serves as simple example.
I have an array of an object Employee. It contains information on the Employee.
public class Employee {
int age
int salary
int yearsWorking
public Employee () {
// constructor...
}
// Accessors
}
What I need to do is organize the Employees by quantiles/percentiles. I have done so by the following:
import org.apache.commons.math.stat.descriptive.rank.Percentile;
public class EmployeeSort {
public void main(String args[]) {
Percentile p = new Percentile();
Employee[] employeeArray = new Employee[100];
// filled employeeArray
double[] ageArray = new double[100];
// filled ageArray with ages from employeeArray
int q = 25; // Percentile cutoff
for (int i = 1; i*q < 100; i++) {
// assign percentile cutoff to some array to contain the values
}
}
}
Now, the problem I have is that I need to organize the Employees first by the percentiles of age, then percentiles of yearsWorking, and finally by percentiles of salary. My Java knowledge is inadequate right now to solve this problem, but the project I was handed was in Java. I am primarily a python guy, so this problem would have been a lot easier in that language. No such luck.
Edit:
So I notice some confusion on whether I need ordering, and some people suggesting use of Comparator. I use Comparator because percentiles requires an ordered list. The problem I am having is how to store the binning of the Employee into their appropriate percentile. To clarify, I need the Employee object binned together into their appropriate percentile by age. Then, within that bin of age, I need to bin all those Employee objects within percentiles for Employee.yearsWorking. Following that, within a given percentile bin of yearsWorking which is within a given percentile bin of Employee.age, I need to create percentile bins of Employee.salary.
You should use ArrayList
in place of Arrays.
ArrayList<Employee> employeeList= new ArrayList<Employee>();
for (int i = 0, i <= employeeArray.length; i++)
employeeList.add(employeeArray[i]);
Now write down custom Comparator
public class EmployeeComparatorByAge<Employee>
{
public int compare(Object o1, Object o2)
{
if (o1 != null && o2!= null && o1 instanceof Employee && o2 instanceof Employee )
return ((Employee)o1).age - ((Employee)o2).age;
else
return -1;
}
}
Similarly you can write for other comparisions.
To sort them now, use:
Collections.sort(employeeList, new EmployeeComparatorByAge<Employee>());
This will solve your problem.
Check out java.util.Comparator<T> (the T in this case would be your Employee type)
You can create different Comparators and use Collections.sortList or Arrays.sort(...) (these methods are pseudo methods - look up the exact versions from the docs)
Instead of an array use an ArrayList to hold your Employees. You can then use Collections.sort() to sort that list. There are two versions of sort(), one of which takes a Comparator that will allow you to sort in your desired order.
You might want to read this link to understand Object Ordering in Java
The next thing that you want to do is use an ArrayList and add all your employees in this. You would then want to do something like this:
ArrayList<Employee> employeeList= new ArrayList<Employee>();
/**
* Add your employee objects to the list
*/
Collections.sort(contacts, new Comparator<Employee>() {
public int compare(Employee emp1, Employee emp2) {
int returnValue;
/**
* Your comparison logic
*/
return returnValue;
}
});
I hope this helps!
Use Arrays.sort() with different Comparator implementations as noted above. Keep in mind that this method (as well as Collections.sort()) operate on the data in place. So if you need to keep getting at different views of the data, you may want to duplicate the array and sort it different ways, and hold onto each view.
This would be more efficient (CPU-wise) than constantly re-sorting the array.
Related
I have an ArrayList<ArrayList<String>> that is a list of couple of values:
John, 12.3
Marcus, 35.0
Sue, 11.4
How to sort the list by amount?
If in this case there is a better way than using an ArrayList of an ArrayList, please tell me and tell me then how to sort it. Thank you.
Instead I will recommend you to use a class and use class like comparable or comparator to sort.
Something like this:
class Person implements Comparable<Person> {
String name;
double amount;
Person(String n, double d) {
name = n;
amount = d;
}
public int compareTo(Person other) {
if (amount != other.amount)
return Double.compare(amount, other.amount);
return name.compareTo(other.name);
}
}
and this is easy to implement and understand.
Use a Comparator:
A comparison function, which imposes a total ordering on some
collection of objects. Comparators can be passed to a sort method
(such as Collections.sort or Arrays.sort) to allow precise control
over the sort order. Comparators can also be used to control the order
of certain data structures (such as sorted sets or sorted maps), or to
provide an ordering for collections of objects that don't have a
natural ordering.
It'd be great if you could add more information about why you are using those values then I am sure a better approach can be suggested.
I solved with this:
Collections.sort(data, new Comparator<ArrayList<String>>() {
#Override
public int compare(ArrayList<String> one, ArrayList<String> two) {
// Replacements for using Double.parseDouble(string) later
String value1 = one.get(1).replace(",", ".");
String value2 = two.get(1).replace(",", ".");
if (Double.parseDouble(value1) < Double.parseDouble(value2))
return -1;
else if (Double.parseDouble(value1) > Double.parseDouble(distanza2))
return 1;
else
return 0;
}
});
I have 2 arrays:
private String[] placeName;
private Double[] miles;
The data in them look like this:
placeName = {"home", "away", "here"};
miles = {111, 11, 3};
The position of the values match to each other. ie home = 111 and away = 11
I need to sort these arrays together so I don't lose how they are matched by the the number- lowest to highest. What is the best way to accomplish this? Do I need to combine the arrays first?
Since the two values are so tightly coupled together I would actually write a custom class to contain the information and then sort those classes instead of playing around with raw arrays. Doing so would leave you open to many possible bugs down the line.
This allows for much better control, data encapsulation and future expansion of what methods or data your class may contain.
public class MyDistance implements Comparable<MyDistance> {
private String placename;
private double mileage;
public MyDistance(String placename, double milage) {
this.placename = placename;
this.milage = milage;
}
public String getPlacename() {
return this.placename;
}
public double getMilage() {
return this.milage;
}
#Override
public int compareTo(MyDistance anotherDistance)
{
return milage.compareTo(anotherDistance.getMilage());
}
}
If you want more flexibility in your sort then instead of having your MyDistance class implement Comparable you can write a custom Comparator<MyDistance> class:
public class DistanceComparator extends Comparator<MyDistance> {
#Override
public int compare(MyDistance dist1, MyDistance dist2) {
return dist1.getMilage().compareTo(dist2.getMilage());
}
}
You can use this Comparator to sort using Collections:
List<MyDistance> distanceList = getDistanceListSomehow();
Collections.sort(distanceList, new DistanceComparator());
You are not restricted to a List, I just used it for explanatory purposes. You should look at the full range of Java Collections types to best choose one that suits your purposes. As a suggestion though, the ArrayList type is easy to use and retains order like you would want.
One way is to create a TreeMap. Assuming you are sorting by miles.
TreeMap tm = new TreeMap<Double, String>();
for (int i=0; i<miles.length; i++) {
tm.put(miles[i], placeName[i]);
}
// tm is already sorted - iterate over it...
NOTE: IF you have places with the same exact distance in miles this will not work. e.g. if you had a "work" that was 11 miles, just like "away", this won't work. You'd probably want some form of MultiMap for that...
Maybe put the arrays in a TreeMap and sort it
SortedMap<Double,String> map = new TreeMap<>();
map.put(111,"home");
map.put(11,"away");
map.put(3,"here");
The elemtns are inserted sorted by their key
I have an array myArray[] of objects MyThing which contains X elements. I need to remove elements belonging to the same group, but leaving one representative of each group.
MyThing class has a field groupId
public class MyThing {
private int groupId;
//...other fields
public int getGroupId(){return groupId;}
//getter and setter
}
So I have to compare groupId integer value of array elements (myArray[x].getGroupId()) and remove all element belonging the same group except the first such element in the array.
This way I will get an array of unique elements with only 1 from the same group. For example, if I have an array with a.getGroupId()=1, b.getGroupId()=2, c.getGroupId()=1 after purification, the array will contain only {a,b}, and c will be removed since it's of the same group as a.
Because this is the custom object, I cannot use Set<T>.
Any ideas?
PS. please let me know if I explained this clearly since it's kind of confusing.
A set by definition doesn't contain any duplicates. A set determines if two items are alike, by using either the objects equals()/compareTo(..) method or by using a Comparator. If you only want unique items in your set, implementing the Comparable interface and overriding equals() is what you want to do. BUT in your case, you're only interested in objects in unique groups, so it's then better to create a custom Comparator for the occasion, which you then supply to the Set, telling it to use it, instead of "natural ordering".
Set<MyThing> myThings = new TreeSet<>(new Comparator<MyThing>() {
#Override
public int compare(MyThing o1, MyThing o2)
{
return o1.getGroupId() - o2.getGroupId();
}
});
myThings.addAll(Arrays.asList(myArray));
After creating the set, you add your entire array into it, using the convinience method addAll(..).
(How the comparator sorts your objects is completely up to you to decide.)
You could loop through your array and use a map to keep track of which IDs have already occurred. Then if one was already added to the set, remove it from the array:
Set<Integer> uniqueIDs = new HashSet<Integer>();
for(MyThing thing : MyThings){
int groupID = thing.getGroupId();
if(!uniqueIDs.add(groupID)){
// DUPLICATE, REMOVE IT
}
}
Use a TreeSet and a custom Comparator class that inspects your objects and treats two with the same group as equal.
http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html
Algorithm psuedocode:
Create TreeSet
Add all array elements to TreeSet
Convert TreeSet back to array
For a sample implementation: see Martin's answer
Just rewrote the Martin's solution because the comparator is broken, it might overflow
Set<MyThing> myThings = new TreeSet<>(new Comparator<MyThing>() {
#Override
public int compare(MyThing o1, MyThing o2) {
return Integer.compare(o1.getGroupId(), o2.getGroupId());
}
});
myThings.addAll(Arrays.asList(myArray));
Why don't you try something like (semi-pseudo-code here):
List<Integer> uniqGroups = new ArrayList<Integer>();
for (int i = 0; i < myArray.length; i++) {
int groupId = myArray[i].getGroupId();
if (!uniqGroups.contains(groupId)) {
// Hasn't been seen before, keep around
uniqGroups.add(groupId);
}
else {
// Already seen, remove or otherwise clean up the array
myArray[i] = null;
}
}
As you just need to distinguish your objects by groupId, you might override the hashCode() and equals() methods in your class:
class MyThing {
private int groupId;
public int getGroupId(){return groupId;}
// new code to add...
#Override
public int hashCode() {
return groupId;
}
#Override
public boolean equals(Object o) {
return (o instanceof MyThing
&& (groupId == ((MyThing)o).groupId));
}
}
and then, use a HashSet<MyThing> class to remove the MyThing objects in myArray with duplicated groupId:
myArray = new HashSet<MyThing>(Arrays.asList(myArray)).toArray(new MyThing[0]);
I'm looping through a List to find a particular entry, then assigning that to a variable and trying to remove it later. It's easier to demo than to explain.
ArrayList<Example> list1 = populate();
Example ex1 = list1.get(0);
Example ex2 = ex1;
list1.remove(ex2);
I know this likely has something to do with Java's inability to handle pointers, but a viable solution would be great.
Edit: To elaborate, this is a brief example of my code rather than giving you the full thing. What I'm doing is iterating through a list to find the lowest 10 numbers. My technique is to go through the list, find the lowest and add it to another list, then remove that number from the original list and repeat. But my list is made of objects which have an int value inside them, rather than a list of integers.
for(0 to 9){
for(0 to list.size){
if(list.get(x) < smallest)
smallest = list.get(x)
}
smallestList.add(smallest);
list.remove(smallest)
}
I would sort the list. Then, I would create a list with those 10 smallest objects and change the original list list1 to contain the remaining objects. Something like:
Collection.sort(list1);
ArrayList<Example> yourSmallestElements = (ArrayList<Example>)(list1.sublist(0, 9).clone());
list1.removeAll(yourSmallestElements);
NOTE: I cloned the sublist because sublist() only returns a view of the list list1, and that's not what you want here.
Your class Example can implement "Comparable" so that you can define how they need to be compared. You will need to implement the method compareTo(). Something like this:
public class Example implements Comparable<Example> {
private int integerVal = <a value>;
public int compareTo(Example exampleObject) {
return exampleObject.integerVal - this.integerVal;
}
}
Have a look at this link, more precisely the class that begins as follows:
public class Fruit implements Comparable<Fruit>{
If you want to sort your objects...
Example e;
int min=-1; // assuming the list has +ve numbers only
for (Example elem : yourList)
{
if ( elem.gtVaribale() <= min ) //assuming you have variable field in your object
{
e = elem;
min = elem.getVariable();
}
}
yourList.remove(e);
//repeat this for remaining elements of the list
//you can create another sorted list, and do sortedList.add(e), so that sortedList
//have objects in ascending order (of the variable you want to sort) of objects you had in yourList
This is just a pseudoCode and I have not compiled it.
Here you will have to override the comparable method for class Example.
You have to let compiler know which way it should compare your e variable to its list's elements so as to remove it.
I have an ArrayList with a number of records and one column contains gas names as CO2 CH4 SO2, etc. Now I want to retrieve different gas names(unique) only without repeatation from the ArrayList. How can it be done?
You should use a Set. A Set is a Collection that contains no duplicates.
If you have a List that contains duplicates, you can get the unique entries like this:
List<String> gasList = // create list with duplicates...
Set<String> uniqueGas = new HashSet<String>(gasList);
System.out.println("Unique gas count: " + uniqueGas.size());
NOTE: This HashSet constructor identifies duplicates by invoking the elements' equals() methods.
You can use Java 8 Stream API.
Method distinct is an intermediate operation that filters the stream and allows only distinct values (by default using the Object::equals method) to pass to the next operation.
I wrote an example below for your case,
// Create the list with duplicates.
List<String> listAll = Arrays.asList("CO2", "CH4", "SO2", "CO2", "CH4", "SO2", "CO2", "CH4", "SO2");
// Create a list with the distinct elements using stream.
List<String> listDistinct = listAll.stream().distinct().collect(Collectors.toList());
// Display them to terminal using stream::collect with a build in Collector.
String collectAll = listAll.stream().collect(Collectors.joining(", "));
System.out.println(collectAll); //=> CO2, CH4, SO2, CO2, CH4 etc..
String collectDistinct = listDistinct.stream().collect(Collectors.joining(", "));
System.out.println(collectDistinct); //=> CO2, CH4, SO2
I hope I understand your question correctly: assuming that the values are of type String, the most efficient way is probably to convert to a HashSet and iterate over it:
ArrayList<String> values = ... //Your values
HashSet<String> uniqueValues = new HashSet<>(values);
for (String value : uniqueValues) {
... //Do something
}
you can use this for making a list Unique
ArrayList<String> listWithDuplicateValues = new ArrayList<>();
list.add("first");
list.add("first");
list.add("second");
ArrayList uniqueList = (ArrayList) listWithDuplicateValues.stream().distinct().collect(Collectors.toList());
ArrayList values = ... // your values
Set uniqueValues = new HashSet(values); //now unique
Here's straightforward way without resorting to custom comparators or stuff like that:
Set<String> gasNames = new HashSet<String>();
List<YourRecord> records = ...;
for(YourRecord record : records) {
gasNames.add(record.getGasName());
}
// now gasNames is a set of unique gas names, which you could operate on:
List<String> sortedGasses = new ArrayList<String>(gasNames);
Collections.sort(sortedGasses);
Note: Using TreeSet instead of HashSet would give directly sorted arraylist and above Collections.sort could be skipped, but TreeSet is otherwise less efficent, so it's often better, and rarely worse, to use HashSet even when sorting is needed.
When I was doing the same query, I had hard time adjusting the solutions to my case, though all the previous answers have good insights.
Here is a solution when one has to acquire a list of unique objects, NOT strings.
Let's say, one has a list of Record object. Record class has only properties of type String, NO property of type int.
Here implementing hashCode() becomes difficult as hashCode() needs to return an int.
The following is a sample Record Class.
public class Record{
String employeeName;
String employeeGroup;
Record(String name, String group){
employeeName= name;
employeeGroup = group;
}
public String getEmployeeName(){
return employeeName;
}
public String getEmployeeGroup(){
return employeeGroup;
}
#Override
public boolean equals(Object o){
if(o instanceof Record){
if (((Record) o).employeeGroup.equals(employeeGroup) &&
((Record) o).employeeName.equals(employeeName)){
return true;
}
}
return false;
}
#Override
public int hashCode() { //this should return a unique code
int hash = 3; //this could be anything, but I would chose a prime(e.g. 5, 7, 11 )
//again, the multiplier could be anything like 59,79,89, any prime
hash = 89 * hash + Objects.hashCode(this.employeeGroup);
return hash;
}
As suggested earlier by others, the class needs to override both the equals() and the hashCode() method to be able to use HashSet.
Now, let's say, the list of Records is allRecord(List<Record> allRecord).
Set<Record> distinctRecords = new HashSet<>();
for(Record rc: allRecord){
distinctRecords.add(rc);
}
This will only add the distinct Records to the Hashset, distinctRecords.
Hope this helps.
public static List getUniqueValues(List input) {
return new ArrayList<>(new LinkedHashSet<>(incoming));
}
dont forget to implement your equals method first
If you have an array of a some kind of object (bean) you can do this:
List<aBean> gasList = createDuplicateGasBeans();
Set<aBean> uniqueGas = new HashSet<aBean>(gasList);
like said Mathias Schwarz above, but you have to provide your aBean with the methods hashCode() and equals(Object obj) that can be done easily in Eclipse by dedicated menu 'Generate hashCode() and equals()' (while in the bean Class).
Set will evaluate the overridden methods to discriminate equals objects.