Sort two dimensional ArrayList - java

I am trying to get the shorest distance and its stop_lat, stop_lon between the incoming latD, longD and the stored one in the stops table. I am storing the lat_stop, lon_stop, distStops in double tow dimensional arrayList. Currently I am getting this error
The method min(Collection, Comparator) in the type Collections is not applicable for the arguments
(List>, new Comparator>(){})
The method sort(List, Comparator) in the type Collections is not applicable for the arguments
(ArrayList>, new Comparator>(){})
Example:
(140.4, 83.346723, 12.567835),
(90.6, 83.0984543, 10.347291),
(6.4, 83.6453974, 12.570937),
(25.7, 83.198472, 13.7364563)
I want to get this set (6.4, 83.6453974, 12.570937)
How can I get the shortest distance and its related stop_lat, stop_lon?
I appreciate any help.
// the stops and arrrivaltimes tables exist.
PreparedStatement preparedLatLong = con
.prepareStatement("SELECT lat, longi, name from stops");
ResultSet rsLatLong = preparedLatLong.executeQuery();
// ArrayList<Double> distanceHistory = new ArrayList<Double>();
ArrayList<List<Double>> distanceHistory = new ArrayList<List<Double>>();
while (rsLatLong.next()) {
double lat_stop = rsLatLong.getDouble("lat");
double lon_stop = rsLatLong.getDouble("longi");
double distStops = haversineDistance(latD, longD, lat_stop,
lon_stop);
distanceHistory.add(Arrays.asList(distStops, lat_stop,
lon_stop));
;
}
//Find the shortest diestance and its related longi and lati
Collections.sort(distanceHistory,
new Comparator<ArrayList<Double>>() {
#Override
public int compare(ArrayList<Double> o1,
ArrayList<Double> o2) {
// TODO Auto-generated method stub
return o1.get(0).compareTo(o2.get(0));
}
}
);

You have defined your distanceHistory list as ArrayList<List<Double>>. This means that each element in this list is a List<Double>.
But then, you defined your comparator as a Comparator<ArrayList<Double>>. This means that it expects the items it compares to be specifically ArrayList<Double>.
When you use Collections.sort, it expects a comparator whose base type is more general than the base type of the collection. And ArrayList<Double> is not more general than List<Double>.
The simple solution is to change the definition of the comparator to Comparator<List<Double>>.
But this design is really not very good. You are supposed to use lists for "similar" things. A list of three doubles that do not represent the same sort of information is not a good design. It would be better to create a small class for this:
private static class StopItem implements Comparable<StopItem> {
double stopLat, stopLon, stopDist;
public StopItem( double stopLat, stopLon, stopDist ) {
this.stopLat = stopLat;
this.stopLon = stopLon;
this.stopDist = stopDist;
}
// Getters, setters...
#Override
public int compareTo( StopItem otherItem ) {
return Double.compare( this.stopDist, otherItem.stopDist );
}
}
You can then create a list of these objects, and use Collections.sort() on it, and you don't need an extra comparator.
For example, here is how you'd fill your list:
List<StopItem> distanceHistory = new ArrayList<>();
while (rsLatLong.next()) {
double latStop = rsLatLong.getDouble("lat");
double lonStop = rsLatLong.getDouble("longi");
double distStop = haversineDistance(latD, longD, latStop, lonStop);
StopItem newItem = new StopItem( latStop, lonStop, distStop );
distanceHistory.add(newItem);
}
And then you can use Collections.sort(distanceHistory).

First of all, the Comparator should take two arguments of type List<Double>:
Collections.sort( distanceHistory,
new Comparator<List<Double>>()
{
#Override
public int compare(List<Double> o1,List<Double> o2 ) {
...
since that's type of the elements of
ArrayList<List<Double>> distanceHistory = new ArrayList<List<Double>>();
(Does your code even compile as it is?)
Secondly you might want to work on your data structure a bit; perhaps a tuple/class with three attributes rather than just a List - right now you're in object denial :-) Eg.
class MyThingy implements Comparable<MyThingy> {
Double lat, lon, dist;
#Override compareTo( MyThingy other ) {
// implement sensibly.
}
}
Then you can just
List<MyThingy> distanceHistory = new ArrayList<MyThingy>();
...
Collections.sort( distanceHistory );
without having to supply an anonymous comparator.
Cheers,

Related

How to Sort a Nested List of strings

I want to sort the list by values in the list. I want to do multisorting based on few parameters in the list. Providing sample example how data looks like.
Note: I don't have feasibility to convert List<List<String>> into a list of objects.
List<List<String>> data = new ArrayList<>();
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<String> list3 = new ArrayList<>();
list1.add("Siva");
list1.add("20");
list1.add("Hyd");
list1.add("TA");
list1.add("India");
list2.add("Suresh");
list2.add("22");
list2.add("Banglore");
list2.add("KA");
list2.add("India");
list3.add("Ramesh");
list3.add("24");
list3.add("Chennai");
list3.add("TN");
list3.add("India");
data.add(list1);
data.add(list2);
data.add(list2);
I want to do multi sorting based on name, age and city.
It's just sample data. List of lists is dynamic. Sorting parameters will also change sometimes.
I want to do sorting on a list of lists of strings only.
Expected Output: List<List<String>> sortedData
Solution by Maintaining your Structure
If you can't really create a class wrapping the data in your nested List (for whatever reason), you could use the collection stream and define the sorted operation's logic as follows:
List<List<String>> listRes = data.stream()
.sorted((x, y) -> {
int res = x.get(0).compareTo(y.get(0)); //Comparing by name
if (res != 0) return res;
res = Integer.valueOf(x.get(1)).compareTo(Integer.valueOf(y.get(1))); //Comparing by age (numeric value)
if (res != 0) return res;
return x.get(2).compareTo(y.get(2)); //Comapring by city
})
.collect(Collectors.toList());
Link to test the code above:
https://ideone.com/RhW1VI
Alternative Solution
However, as it has been pointed out in the comments, a better approach would be to create a custom class representing your data in the nested List. Perhaps a simple record if you're using Java 14 or later with a factory method to retrieve an instance of your class from a nested List.
Then, with a stream you could map each nested list to your custom class and sort it with a Comparator.
Here is a snippet of the implementation:
public static void main(String[] args) {
List<List<String>> data = /* ... your initialization ... */
List<MyClass> listSorted = data.stream()
.map(list -> MyClass.createMyClass(list))
.sorted(Comparator.comparing(MyClass::getName).thenComparing(MyClass::getAge).thenComparing(MyClass::getCity))
.collect(Collectors.toList());
System.out.println(listSorted);
}
Mapping record
record MyClass(String name, int age, String city, String code, String country) {
public static MyClass createMyClass(List<String> list) {
if (list == null || list.size() < 5) {
return null;
}
MyClass mc = new MyClass();
mc.name = list.get(0);
mc.age = Integer.valueOf(list.get(1));
mc.city = list.get(2);
mc.code = list.get(3);
mc.country = list.get(4);
return mc;
}
}
Here there is also a link with both implementations:
https://ideone.com/UK9trV
In order to impose the order of lists inside a nested list need to define a Comparator.
As you've said that contents of the list will can't be predicted in advance, I assume that nested lists might be of arbitrary size and their sizes might not be equal.
A comparator that can handle such case might be written like that:
Comparator<List<String>> listComparator = new Comparator<>() {
#Override
public int compare(List<String> o1, List<String> o2) {
int limit = Math.min(o1.size(), o2.size());
for (int i = 0; i < limit; i++) {
int localResult = o1.get(i).compareTo(o2.get(i));
if (localResult != 0)
return localResult;
}
return o1.size() - o2.size();
}
};
In order to sort the list, you can apply method sort() on it (available with Java 8+) which expects a comparator:
data.sort(listComparator);
And you can make a defensive copy of the list before applying sort(), if its initial order might be useful for you:
List<List<String>> initialOrder = new ArrayList<>(data);
data.sort(listComparator);

arraylist sort and convert to String[]

I have a simple class which is auto-generated and use to store responses from Retrofit. Anyway in the final step I would like to use row content, sort every element by position from highest to lowest and after sorting convert it to String[] with name only. How can I do that in the most efficient way?
public class RowModel implements Comparable<RowModel>{
private String name;
private double position;
public double getPosition() {
return position;
}
public void setPosition(float position) {
this.position = position;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public RowModel(String name, double position) {
this.name = name;
this.position = position;
}
}
After my search online I found this method to execute sorting but I'm not sure that the results are correct. And of course I still don't know how to convert sorted names to String[] at the final step:
#Override
public int compareTo(RowModel rowModel) {
double comparePosition = ((RowModel) rowModel).getPosition();
return (int) (this.position- comparePosition);
}
Your compareTo method won't work as you are comparing double values and it will fail when integral parts of two values are same (i.e. when comparing 1.5 & 1.2 your comparator will return 0 but it should return 1).
for that you can use Double.compare(d1, d2). you don't need to cast rowModel, too. Change your method as follow:
#Override
public int compareTo(RowModel rowModel) {
double comparePosition = rowModel.getPosition();
return Double.compare(this.position, comparePosition);
}
For Sorting you can use Collections.sort(rowModels).
To convert this sorted list to list of names, you can use Stream API.
Assuming you have Sorted List of RowModel with name rowModels.
List<String> names = rowModels.stream().map(RowModel::getName).collect(Collectors.toList());
If you want to convert this list to array,
String[] array = names.toArray(new String[names.size()]);
or directly,
String[] array = rowModels.stream().map(RowModel::getName).toArray(String[]::new);
Assuming a list of rows RowModel in a variable named rows, you can easily do that using the Stream API:
List<String> names = rows.stream().sorted()
.map(row -> row.getName())
.collect(Collectors.toList());
The intermediate step sorted() makes sure the stream is sorted, according to the Comparable implementation in your RowModel class, in this case.
Side note: Your compareTo implementation can be improved as it may be losing precision (casting double to int has the side effect of treating as equal two objects that aren't):
#Override
public int compareTo(RowModel rowModel) {
double comparePosition = ((RowModel) rowModel).getPosition();
double diff = this.position - comparePosition;
return diff == 0 ? 0 :
(diff > 0 ? 1 : -1);
}
List<RowModel> sortedModels = new ArrayList<>(models);
Collections.sort(sortedModels);
return sortedModels.toArray(new RowModel[0]);
Edit:
To actually return array of just names rather than whole RowModels, you could do something like this:
String[] result = new String[sortedModels.size()];
for (int i = 0; i < sortedModels.size(); i++) {
result.[i] = sortedModels.get(i).getName();
}
There is probably some shorter way to do this using Java 8 streams, but I'll leave that to someone who actually works with them.
Use util method of java.util.Collections class, i.e
Collections.sort(list)
If you want to sort custom object you can use
Collections.sort(List<T> list, Comparator<? super T> c)
see collections api

Sorting an ArrayList<List> in Java [duplicate]

This question already has answers here:
Sort ArrayList of custom Objects by property
(29 answers)
Closed 6 years ago.
I have an ArrayList<List> where the Lists have string values that hold a name and then a double converted to a string.
Example:
List<String> list = New List;
list.add("Abraham");
list.add(String.valueOf(0.65));
List<String> list2 = New List;
list2.add("Bowers");
list2.add(String.valueOf(0.89));
ArrayList<List> arrayList = new ArrayList<>();
arrayList.add(list);
arrayList.add(list2);
How can I sort the ArrayList in descending order by the value of the double?
Use Collections.sort(arrayList, instanceOfYourOwnImplemenationOfComparator) after having a custom implementation of Comparator<ArrayList>.
Or better, Java is an Object oriented language so create a class dedicated to the storage of your String+double and make it comparable.
class MyClass implements Comparable<MyClass> {
private String word;
private double score;
MyClass(String word, double score) {
this.word = word;
this.score = score;
}
#Override
public int compareTo(MyClass o) {
return (int) Math.round(Math.signum(score - o.score));
}
}
I would change your implementation and create a class containing two fields, a String and a Double one. This class would implement the Comparable interface and its compareTo would be based on the double alone. Something like
public class MyClass implements Comparable<MyClass> {
private double value;
private String name;
/*Constructors, setters and getters*/
public int compareTo(MyClass o) {
return(new Double(value)).compareTo(myO.getValue));
}
}
Then, your code would become:
ArrayList<List> arrayList = new ArrayList<>();
arrayList.add(new MyClass("abraham",0.65));
arrayList.add(new MyClass("bowers", 0.89));
Collections.sort(arrayList);
I just typed the code, but I believe the idea is pretty straightforward.
I hope it helps.
You should be using a class. Let's call it Foo
Then you would do
List<Foo> mylist = new ArrayList<Foo>();
mylist.add (new Foo("Abraham", 0.65);
mylist.add (new Foo("Bowers", 0.89);
// etc
I'll leave it as an excercise for you to write the Foo class. It should implement Comparable
class Foo implements Comparable {
// TODO
Finally
Collections.sort(mylist);
In Java 8 it may be done with streams like this
arrayList.stream()
.sorted((l1, l2) -> Doulbe.valueOf(l1.get(1)) - Doulbe.valueOf(l2.get(1)))
.collect(Collectors.toList())
Here l1 and l2 are your sublists and each 2nd elements of them are your numbers. The used comparator subtracts one number from another for making decision in what order your sublists should be put into output arrayList

Sorting List of lists/ Arraylists/ Hashmaps with multiple values in Java

I want to create a table like structure in Java as shown in the image
Table structure
Though I am not an expert in Java, I have tried to implement it using Arraylist structure as follows:
List<List<Double>> dataList = new ArrayList<List<Double>>();
for(int x = 0; x < n; x++){
List<Double> tempList = new ArrayList<Double>();
dataList.add(tempList);
}
for(int y = 0; y < n; y++){
double execution = exectime[y];
double cost= cost[y];
dataList.get(y).add(execution);
dataList.get(y).add(cost);
}
for (int z=0;z<dataList.size();z++) {
Double v1=dataList.get(z).get(0);
Double v2=dataList.get(z).get(1);
System.out.println("ID"+z +" Execution time:" + v1 + "cost: " + v2);
}
Where the values of 'n', 'exectime[n]' and 'cost[n]' will be read from a file and 'n' is the total number of 'ids' that needs to be created.
After creating the table, I want to sort it based on the 'execution time' value and 'cost' value, both increasing and decreasing order. Please help me in this regards.
#snovelli's answer about using a class to encapsulate your data is a good point.
If you are using Java 8, you can easily create and chain comparators that use accessors.
For sorting a list of objects, it might look something like:
List<ExecutionTimeData> dataList = new ArrayList<>();
dataList.sort(Comparator
.comparing(ExecutionTimeData::getExecutionTime)
.thenComparing(ExecutionTimeData::getCost));
Sorting by execution time, followed by cost.
You could also use this to sort a List<List<Double>> if you really wanted to.
List<List<Double>> doubleListList = new ArrayList<>();
doubleListList.sort(Comparator
.comparing((List<Double> l) -> l.get(0))
.thenComparing(l -> l.get(1)));
Sorting by element 0 of the list, followed by element 1.
Or for sorting in reverse order:
List<ExecutionTimeData> dataList = new ArrayList<>();
dataList.sort(Comparator
.comparing(ExecutionTimeData::getExecutionTime).reversed()
.thenComparing(ExecutionTimeData::getCost).reversed());
Use Collections.sort() with Comparator.
However, you will loss your ID information because it is based on your index of the ArrayList. Therefore, if you use this method and want to keep you ID information, you need to add() ID to your ArrayList just like execution and cost.
Comparator<List<Double>> ORDER = new Comparator<List<Double>>() {
#Override
public int compare(List<Double> lhs, List<Double> rhs) {
if (lhs.get(1) < rhs.get(1)) return -1;
if (lhs.get(1) == rhs.get(1)) return 0;
return 1;
}
};
Collections.sort(dataList, ORDER);
In above code, your dataList will sorted with cost, because it is at the index 1 of the ArrayList.
However, the better way (in readability) is you put your column into a Class, not just a ArrayList. For example, you can create a Class like this:
class Information {
private int id;
private double execution;
private double cost;
Information(int id, double execution, double cost) {
this.id = id;
this.execution = execution;
this.cost = cost;
}
}
And implement static Comparator inside that class. It will improve the readability of your code.
I think You should use a Chained Comparator to implement sorting using multiple attributes. Because If you use a single Comparator Individually It will sort the data according to its own Compare() Method Implementation.
Better to Go with Chained Comparator which sort your data on multiple attribute ... Try the Following Link ==> Sorting a list by multiple attributes example
Use Collections as List < RegisterType > , RegisterType is created according to the type of registers present in the table (ex: with 3 double atributes)
Implement the Comparator interface Comparator< RegisterType >
Override the compare( RegisterType o1, RegisterType o2) method the way you want (define how to sort 2 elements of type RegisterType)
Inkove Collections.sort(List< RegisterType > list, ComparatorClass)
Then you will have your collection list sorted the way you want.
A table is a way to represent a list of objects, why not use a list of object then?
I think you want to have a SortedSet of a class that you could define as:
public class ExecutionTimeData{
private final long id;
private final long executionTime;
private final int cost;
public ExecutionTimeData(long id, long executionTime, int cost){
this.id = id;
this.executionTime = executionTime;
this.cost = cost;
}
/* Getters */
}
Then you will simply have an unsorted list like
List<ExecutionTimeData> unsortedList = new ArrayList<>();
As pointed out from #VikrantKashyap to order the list with both value and cost you then must implement a Chained Comparator
public class ExecutionTimeDataChainedComparator implements Comparator<ExecutionTimeData> {
private List<Comparator<ExecutionTimeData>> listComparators;
#SafeVarargs
public ExecutionTimeDataChainedComparator (Comparator<ExecutionTimeData>... comparators) {
this.listComparators = Arrays.asList(comparators);
}
#Override
public int compare(ExecutionTimeData etd1, ExecutionTimeData etd2) {
for (Comparator<ExecutionTimeData> comparator : listComparators) {
int result = comparator.compare(etd1, etd2);
if (result != 0) {
return result;
}
}
return 0;
}
}
And implement the comparators like this
public class ExecutionTimeDataCostComparator implements Comparator<ExecutionTimeData > {
#Override
public int compare(ExecutionTimeData a, ExecutionTimeData b) {
return b.getCost() > a.getCost()?-1:1;
}
}
public class ExecutionTimeDataExecutionComparator implements Comparator<ExecutionTimeData > {
#Override
public int compare(ExecutionTimeData a, ExecutionTimeData b) {
return b.getExecutionTime() > a.getExecutionTime()?-1:1;
}
}
And of course you can find out an easy way to invert the order by instantiating the comparators providing ASCENDING or DESCENDING order

First N values of a Map<K, V> sorted by value

I have a list of Strings. I want to evaluate each string based on a function that returns a double. Then I want the first 5 strings, based on their calculated values. If there are fewer than 5, I want all of them (in order). Let's say the strings are chemical compounds and the function computes the mass. The function is computationally expensive; I need to evaluate it once per string. (I'm just making up data here, though.)
H2O => 18.5
C12H11O22 => 109.1
HeNe => 32.0
H2SO4 => 54.37
HCl => 19.11
4FeO3 => 82.39
Xe6 => 281.9
The program should return the first five strings arranged in order by their respective values. For this sample data: H20, HCl, HeNe, H2SO4, 4FeO3. Actually, I don't really care about the order; I just need the five lowest in any order.
I thought about how I'd do this in Perl. It's just a few lines:
foreach $s (#str) {
$strmap{$s} = f($s);
}
#sorted = sort { $strmap{$a} <=> $strmap{$b} } keys %strmap;
return #sorted[0, 4]
But I need to do it in Java. And it's driving me crazy.
First I tried populating a HashMap<String, Double>, then using Collections.sort with a custom comparator, just like the Perl version. But scoping on the Comparator prevented it from referring to the HashMap to look up the values.
Then I tried a TreeMap<String, Double>, but it only sorts by key and no amount of coercing could get it to order the entries by value.
So I tried a TreeMap<Double, String>. It will discard entries with the same Double. However, the likelihood of having Strings that map to the same Double is low, so I pressed forward. Adding the entries to the TreeMap is no problem, but I ran into issues trying to extract the values from it.
TreeMap supplies a method called subMap, but its parameters are the keys that delimit the subset. I don't know what they are; I just want the first five of them. So I tried using the values method to get all the values out of the TreeMap, hoping they'd be in order. Then I can just get the first ten.
ArrayList<String> strs = (ArrayList<String>)(treemap.values());
return new ArrayList<String>(strs.subList(0, 5));
Nope. Runtime error: cannot cast TreeMap$Values to ArrayList.
List<String> strs = (List<String>)(treemap.values());
return new ArrayList<String>(strs.subList(0, 5));
Same. Runtime error trying to do the cast. OK, let's just assign to a Collection...
Collection<String> strs = treemap.values();
return new ArrayList<String>(strs.subList(0, 5));
Sorry, subList isn't a method of Collection.
Collection<String> strs = treemap.values();
ArrayList<String> a = new ArrayList<String>(strs);
return new ArrayList<String>(a.subList(0, 5));
Finally, something that works! But two extra data structures just to get the first five elements? And I'm not too wild about using Double as the key for TreeMap.
Is there a better solution?
I don't think you'll get more compact than the three lines above, not in Java.
Apart from that, I have the impression that a Map as a data structure is the wrong choice in the first place, since you do not seem to need by-string lookups (UNLESS you want in some way deal with multiple occurences of strings, but you didn't say so). An alternative approach would be to declare your own comparable data record class:
private static class Record implements Comparable<Record> {
// public final fields ok for this small example
public final String string;
public final double value;
public Record(String string, double value) {
this.string = string;
this.value = value;
}
#Override
public int compareTo(Record other) {
// define sorting according to double fields
return Double.compare(value, other.value);
}
}
// provide size to avoid reallocations
List<Record> records = new ArrayList<Record>(stringList.size());
for(String s : stringList)
records.add(new Record(s, calculateFitness(s));
Collections.sort(records); // sort according to compareTo method
int max = Math.min(10, records.size()); // maximum index
List<String> result = new ArrayList<String>(max);
for(int i = 0; i < max; i++)
result.add(records.get(i).string);
return result;
This is now much more verbose than the three lines above (this is Java, after all), but also includes the code that would be required to insert the key/value pairs into the map.
Would something like the following work for you?
Note that I've assumed you don't require the double value other than to sort the data.
public static void main(String[] args) throws Exception {
List<String> data = new ArrayList<>(Arrays.asList("t", "h", "i", "s", "i", "s", "t", "e", "s", "t", "d", "a", "t", "a"));
Collections.sort(data, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
double o1Value = evaluate(o1);
double o2Value = evaluate(o2);
return Double.compare(o1Value, o2Value);
}
});
List<String> result = data.subList(0, 10); // Note the end point is exclusive
for (String s : result) {
System.out.println(s);
}
}
private static double evaluate(String s) {
return s.codePointAt(0); // Nonsense, I know
}
This example prints:
a
a
d
e
h
i
i
s
s
s
Why don't you just create a class to combine the String, Double and function that does the calculation - something like:
public Thing implements Comparable<Thing>
{
private String s;
private Double d;
public Thing(String s)
{
this.s = s;
this.d = calculateDouble(s);
}
public String getString()
{
return this.s;
}
public Double getDouble()
{
return this.d;
}
public int compareTo(Thing other)
{
return getDouble().compareTo(other.getDouble());
}
public Double calculateDouble(String s)
{
...
}
}
Then all you need is a List<Thing>, Collections.sort and List.subList.

Categories

Resources