I need to sort a java list containing objects of type Hotel
List<Hotel> hotelList = new ArrayList<>();
Inside the class I do have the method
#Override
public List<Room> getAvailableRooms() {
return this.rooms;
}
I need to sort my hotelList by the price attribute found in Room class.
Any suggestions?
You should either use a Comparator or implement the Comparable interface
public class Foo implements Comparable<ToSort> {
private int val;
public Foo(int val){
this.val = val;
}
#Override
public int compareTo(ToSort f) {
if (val > f.val) {
return 1;
}
else if (val < f.val) {
return -1;
}
else {
return 0;
}
}
Read more here
https://dzone.com/articles/sorting-java-arraylist
So I've seen, in many places, calling methods of a class like:
SomeClass obj = new SomeClass();
obj.addX(3).addY(4).setSomething("something").execute();
I don't think I completely understand how that works. Is each method independent of each other, so the above is equal to:
obj.addX(3);
obj.addY(4);
obj.addSomething("something");
obj.execute();
Or are they designing their class structure in some other fashion that allows for this. If they are how are they designing their classes to support this?
Also, does that have a specific name? Or is this just calling methods on a class?
That would be method chaining. It can do one of two things.
Each call to a method returns this which allows you to continue to call methods on the original instance.
public class SomeClass
{
private int _x = 0;
private int _y = 0;
private String _something = "";
public SomeClass addX(int n)
{
_x += n;
return this;
}
public SomeClass addY(int n)
{
_y += n;
return this;
}
public SomeClass setSomething(String something)
{
_something = something;
return this;
}
// And so on, and so on, and so on...
}
Each method call returns a new instance of the class with everything copied/updated appropriately. This makes the class immutable (so you don't accidentally modify something that you didn't mean to).
public class SomeClass
{
private int _x = 0;
private int _y = 0;
private String _something = "";
public SomeClass(int x, int y, String something)
{
_x = x;
_y = y;
_something = something;
}
public SomeClass addX(int n)
{
return new SomeClass(_x + n, _y, _something);
}
public SomeClass addY(int n)
{
return new SomeClass(_x, _y + n, _something);
}
public SomeClass setSomething(String something)
{
return new SomeClass(_x, _y, something);
}
// And so on, and so on, and so on...
}
Some people have also mentioned Fluent Interfaces. Fluent Interfaces utilize method chaining to create an API that provides something along the lines of a Domain Specific Language which can make code read much more clearly. In this case, your example doesn't quite qualify.
they modify object's state and return the same object back mostly
class Number{
int num;
public Number add(int number){
num+=number;
return this;
}
}
you can call it like
new Number().add(1).add(2);
most of the time the use case is to return new Object to support immutability
Each of those methods return an instance. For example, the call to
obj.addX(3)
will return the same instance obj, so the call
obj.addX(3).addY(4)
will be equivalent to
obj.addY(4)
This is called method chaining.
The methods are implemented like this:
public SomeClass addX(int i) {
// ...
return this; // returns the same instance
}
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Test1 abc = new Test1();
abc.add1(10, 20).sub1(40, 30).mul1(23, 12).div1(12, 4);
}
public Test1 add1(int a, int b)
{
int c = a + b;
System.out.println("Freaking Addition output : "+c);
return this;
}
public Test1 sub1(int a, int b)
{
int c = a - b;
System.out.println("Freaking subtraction output : "+c);
return this;
}
public Test1 mul1(int a, int b)
{
int c = a * b;
System.out.println("Freaking multiplication output : "+c);
return this;
}
public Test1 div1(int a, int b)
{
int c = a / b;
System.out.println("Freaking divison output : "+c);
return this;
}
}
I have a class called x which is a array list and needs to be sorted in Decreasing order by Value.
My Class-
public static class x
{
public int id;
public double value;
public x(int _id, double _value)
{
id = _id;
value = _value;
//System.out.println(Integer.toString(id));
}
public Integer getID(){
return id;
}
public double getValue(){
return value;
}
//Sorting
public static Comparator<x> getComparator(SortParameter... sortParameters) {
return new xComparator(sortParameters);
}
public enum SortParameter {
VAL_DESCENDING
}
private static class xComparator implements Comparator<x> {
private SortParameter[] parameters;
private xComparator(SortParameter[] parameters) {
this.parameters = parameters;
}
public int compare(x o1, x o2) {
int comparison;
for (SortParameter parameter : parameters) {
switch (parameter) {
case VAL_DESCENDING:
comparison = o2.id - o1.id;
if (comparison != 0) return comparison;
break;
}
}
return 0;
}
}
}
I Call it like:
cp = x.getComparator(x.SortParameter.VAL_DESCENDING);
Collections.sort(attr1, cp);
attr1 is my array list
Just for Reference I am following this
I am getting error:
cannot find symbol : variable cp
I am a newbie to java :(
try using Comparator<x> cp = x.getComparator(x.SortParameter.VAL_DESCENDING); to declare it. you can not use a variable until it is declared
I wondered how I could change ordering-direction only on some contitional. In my case Strings starting with 'BB' should be ordered in the other direction, everything else should be ordered as usual.
My Test-Class:
public class StringTest {
public static void main(String[] args) {
SomeClass someClass1= new SomeClass("AA");
SomeClass someClass2= new SomeClass("AB");
SomeClass someClass3= new SomeClass("CB4");
SomeClass someClass4= new SomeClass("BB7");
SomeClass someClass5= new SomeClass("BB9");
SomeClass someClass6= new SomeClass("BB3");
SomeClass someClass7= new SomeClass("CB3");
List<SomeClass> list = new ArrayList<SomeClass>();
list.add(someClass1);
list.add(someClass2);
list.add(someClass3);
list.add(someClass4);
list.add(someClass5);
list.add(someClass6);
list.add(someClass7);
Collections.sort(list);
for (SomeClass someClass : list) {
System.out.println(someClass.getSomeField());
}
}
}
My Comparator:
public class SomeClass implements Comparable<SomeClass>
{
private String someField;
public int compareTo(final SomeClass o)
{
int res = 0;
if (someField.startsWith("BB"))
{
res = o.someField.compareTo(someField);
}
else
{
res = someField.compareTo(o.someField);
}
return res;
}
}
My desired output:
AA
AB
BB9
BB7
BB3
CB3
CB4
The actual result so far:
AA
AB
CB3
BB9
BB7
BB3
CB4
Jonny
You need to make sure your Comparator applies the different sorting only when both elements start with "BB". Right now your Comparator applies the different sorting even if you compare "BB9" with "CB3" and therefore the latter is being sorted in front of BB9.
public class SomeClass implements Comparable<SomeClass>
{
private String someField;
public int compareTo(final SomeClass o)
{
int res = 0;
if (someField.startsWith("BB") && o.someField.startsWith("BB"))
{
res = o.someField.compareTo(someField);
}
else
{
res = someField.compareTo(o.someField);
}
return res;
}
}
if(someField.startsWith("BB") && o.someField.startsWith("BB")))
Try this change in your compareTo method which may solve your problem.
I have a class, and list of instances, that looks something like this (field names changed to protect the innocent/proprietary):
public class Bloat
{
public long timeInMilliseconds;
public long spaceInBytes;
public long costInPennies;
}
public class BloatProducer
{
final private List<Bloat> bloatList = new ArrayList<Bloat>();
final private Random random = new Random();
public void produceMoreBloat()
{
int n = bloatList.size();
Bloat previousBloat = (n == 0) ? new Bloat() : bloatList.get(n-1);
Bloat newBloat = new Bloat();
newBloat.timeInMilliseconds =
previousBloat.timeInMilliseconds + random.nextInt(10) + 1;
newBloat.spaceInBytes =
previousBloat.spaceInBytes + random.nextInt(10) + 1;
newBloat.costInPennies =
previousBloat.costInPennies + random.nextInt(10) + 1;
bloatList.add(newBloat);
}
/* other fields/methods */
public boolean testMonotonicity()
{
Bloat previousBloat = null;
for (Bloat thisBloat : bloatList)
{
if (previousBloat != null)
{
if ((previousBloat.timeInMilliseconds
>= thisBloat.timeInMilliseconds)
|| (previousBloat.spaceInBytes
>= thisBloat.spaceInBytes)
|| (previousBloat.costInPennies
>= thisBloat.costInPennies))
return false;
}
previousBloat = thisBloat;
}
return true;
}
BloatProducer bloatProducer;
The list bloatList is kept internally by BloatProducer and is maintained in such a way that it only appends new Bloat records, does not modify any of the old ones, and each of the fields is monotonically increasing, e.g. bloatProducer.testMonotonicity() would always return true.
I would like to use Collections.binarySearch(list,key,comparator) to search for the Bloat record by either the timeInMilliseconds, spaceInBytes, or costInPennies fields. (and if the number is between two records, I want to find the previous record)
What's the easiest way to write a series of 3 Comparator classes to get this to work? Do I have to use a key that is a Bloat object with dummy fields for the ones I'm not searching for?
You'll need to write a separate comparator for each field you want to compare on:
public class BloatTimeComparator implements Comparator<Bloat> {
public int compare(Bloat bloat1, Bloat bloat2) {
if (bloat1.timeInMilliseconds > bloat2.timeInMilliseconds) {
return 1;
} else if (bloat1.timeInMilliseconds < bloat2.timeInMilliseconds) {
return -1;
} else {
return 0;
}
}
}
And so on for each property in Bloat you want to compare on (you'll need to create a comparator class for each). Then use the Collections helper method:
Collections.binarySearch(bloatList, bloatObjectToFind,
new BloatTimeComparator());
From the Java documentation for the binarySearch method, the return value will be:
the index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the list: the index of the first element greater than the key, or list.size() if all elements in the list are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found.
Which is the index you specified that you wanted.
You will need to have 3 separate Comparators if you want to search by each of the 3 properties.
A cleaner option would be to have a generic Comparator which receives a parameter which tells it by which field to compare.
A basic generic comparator should look something like this:
public class BloatComparator implements Comparator<Bloat>
{
CompareByEnum field;
public BloatComparator(CompareByEnum field) {
this.field = field;
}
#Override
public int compare(Bloat arg0, Bloat arg1) {
if (this.field == CompareByEnum.TIME){
// compare by field time
}
else if (this.field == CompareByEnum.SPACE) {
// compare by field space
}
else {
// compare by field cost
}
}
}
Here's a test-driven approach to writing the first comparator:
public class BloatTest extends TestCase{
public class Bloat {
public long timeInMilliseconds;
public long spaceInBytes;
public long costInPennies;
public Bloat(long timeInMilliseconds, long spaceInBytes, long costInPennies) {
this.timeInMilliseconds = timeInMilliseconds;
this.spaceInBytes = spaceInBytes;
this.costInPennies = costInPennies;
}
}
public void testMillisecondComparator() throws Exception {
Bloat a = new Bloat(5, 10, 10);
Bloat b = new Bloat(3, 12, 12);
Bloat c = new Bloat(5, 12, 12);
Comparator<Bloat> comparator = new MillisecondComparator();
assertTrue(comparator.compare(a, b) > 0);
assertTrue(comparator.compare(b, a) < 0);
assertEquals(0, comparator.compare(a, c));
}
private static class MillisecondComparator implements Comparator<Bloat> {
public int compare(Bloat a, Bloat b) {
Long aTime = a.timeInMilliseconds;
return aTime.compareTo(b.timeInMilliseconds);
}
}
}
If you want to leverage the binary search for all three properties, you have to create comparators for them and have additional Lists or TreeSets sorted by the comparators.
test program (MultiBinarySearch.java) to see if these ideas work properly (they appear to):
package com.example.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
class Bloat
{
final public long timeInMilliseconds;
final public long spaceInBytes;
final public long costInPennies;
static final private int N = 100;
public Bloat(long l1, long l2, long l3) {
timeInMilliseconds = l1;
spaceInBytes = l2;
costInPennies = l3;
}
public Bloat() { this(0,0,0); }
public Bloat moreBloat(Random r)
{
return new Bloat(
timeInMilliseconds + r.nextInt(N) + 1,
spaceInBytes + r.nextInt(N) + 1,
costInPennies + r.nextInt(N) + 1
);
}
public String toString() {
return "[bloat: time="+timeInMilliseconds
+", space="+spaceInBytes
+", cost="+costInPennies
+"]";
}
static int compareLong(long l1, long l2)
{
if (l2 > l1)
return -1;
else if (l1 > l2)
return 1;
else
return 0;
}
public static class TimeComparator implements Comparator<Bloat> {
public int compare(Bloat bloat1, Bloat bloat2) {
return compareLong(bloat1.timeInMilliseconds, bloat2.timeInMilliseconds);
}
}
public static class SpaceComparator implements Comparator<Bloat> {
public int compare(Bloat bloat1, Bloat bloat2) {
return compareLong(bloat1.spaceInBytes, bloat2.spaceInBytes);
}
}
public static class CostComparator implements Comparator<Bloat> {
public int compare(Bloat bloat1, Bloat bloat2) {
return compareLong(bloat1.costInPennies, bloat2.costInPennies);
}
}
enum Type {
TIME(new TimeComparator()),
SPACE(new SpaceComparator()),
COST(new CostComparator());
public Comparator<Bloat> comparator;
Type(Comparator<Bloat> c) { this.comparator = c; }
}
}
class BloatProducer
{
final private List<Bloat> bloatList = new ArrayList<Bloat>();
final private Random random = new Random();
public void produceMoreBloat()
{
int n = bloatList.size();
Bloat newBloat =
(n == 0) ? new Bloat() : bloatList.get(n-1).moreBloat(random);
bloatList.add(newBloat);
}
/* other fields/methods */
public boolean testMonotonicity()
{
Bloat previousBloat = null;
for (Bloat thisBloat : bloatList)
{
if (previousBloat != null)
{
if ((previousBloat.timeInMilliseconds
>= thisBloat.timeInMilliseconds)
|| (previousBloat.spaceInBytes
>= thisBloat.spaceInBytes)
|| (previousBloat.costInPennies
>= thisBloat.costInPennies))
return false;
}
previousBloat = thisBloat;
}
return true;
}
public int searchBy(Bloat.Type t, Bloat key)
{
return Collections.binarySearch(bloatList, key, t.comparator);
}
public void showSearch(Bloat.Type t, Bloat key)
{
System.out.println("Search by "+t+": ");
System.out.println(key);
int i = searchBy(t,key);
if (i >= 0)
{
System.out.println("matches");
System.out.println(bloatList.get(i));
}
else
{
System.out.println("is between");
i = -i-1;
Bloat b1 = (i == 0) ? null : bloatList.get(i-1);
System.out.println(b1);
Bloat b2 = (i >= bloatList.size()) ? null : bloatList.get(i);
System.out.println("and");
System.out.println(b2);
}
}
}
public class MultiBinarySearch {
private static int N = 1000;
public static void main(String[] args)
{
BloatProducer bloatProducer = new BloatProducer();
for (int i = 0; i < N; ++i)
{
bloatProducer.produceMoreBloat();
}
System.out.println("testMonotonicity() returns "+
bloatProducer.testMonotonicity());
Bloat key;
key = new Bloat(10*N, 20*N, 30*N);
bloatProducer.showSearch(Bloat.Type.COST, key);
bloatProducer.showSearch(Bloat.Type.SPACE, key);
bloatProducer.showSearch(Bloat.Type.TIME, key);
key = new Bloat(-10000, 0, 1000*N);
bloatProducer.showSearch(Bloat.Type.COST, key);
bloatProducer.showSearch(Bloat.Type.SPACE, key);
bloatProducer.showSearch(Bloat.Type.TIME, key);
}
}