How to re-order a List<String> - java

I have created the following method:
public List<String> listAll() {
List worldCountriesByLocal = new ArrayList();
for (Locale locale : Locale.getAvailableLocales()) {
final String isoCountry = locale.getDisplayCountry();
if (isoCountry.length() > 0) {
worldCountriesByLocal.add(isoCountry);
Collections.sort(worldCountriesByLocal);
}
}
return worldCountriesByLocal;
}
Its pretty simple and it returns a list of world countries in the users locale. I then sort it to get it alphabetic. This all works perfectly (except I seem to occasionally get duplicates of countries!).
Anyway, what I need is to place the US, and UK at the top of the list regardless. The problem I have is that I can't isolate the index or the string that will be returned for the US and UK because that is specific to the locale!
Any ideas would be really appreciated.

Anyway, what I need is to place the US, and UK at the top of the list regardless. The problem I have is that I can't isolate the index or the string that will be returned for the US and UK because that is specific to the locale!
It sounds like you should implement your own Comparator<Locale> to compare two locales with the following steps:
If the locales are the same, return 0
If one locale is the US, make that "win"
If one locale is the UK, make that "win"
Otherwise, use o1.getDisplayCountry().compareTo(o2.getDisplayCountry()) (i.e. delegate to existing behaviour)
(This will put the US before the UK.)
Then call Collections.sort with an instance of your custom comparator.
Do all of this before extracting the country names - then extract them from the sorted list.

You could also use a TreeSet to eliminate duplicates and your own Comparator to bring US and GB up to the start.
You are getting duplicates (which this will eliminate) because there are often more than one locale per country. There is a US(Spanish) as well as a US(English) and there are three Switzerlands (French, German and Italian) for example.
public class AllLocales {
// Which Locales get priority.
private static final Locale[] priorityLocales = {
Locale.US,
Locale.UK
};
private static class MyLocale implements Comparable<MyLocale> {
// My Locale.
private final Locale me;
public MyLocale(Locale me) {
this.me = me;
}
// Convenience
public String getCountry() {
return me.getCountry();
}
#Override
public int compareTo(MyLocale it) {
// No duplicates in the country field.
if (getCountry().equals(it.getCountry())) {
return 0;
}
// Check for priority ones.
for (int i = 0; i < priorityLocales.length; i++) {
Locale priority = priorityLocales[i];
// I am a priority one.
if (getCountry().equals(priority.getCountry())) {
// I come first.
return -1;
}
// It is a priority one.
if (it.getCountry().equals(priority.getCountry())) {
// It comes first.
return 1;
}
}
// Default to straight comparison.
return getCountry().compareTo(it.getCountry());
}
}
public static List<String> listAll() {
Set<MyLocale> byLocale = new TreeSet();
// Gather them all up.
for (Locale locale : Locale.getAvailableLocales()) {
final String isoCountry = locale.getDisplayCountry();
if (isoCountry.length() > 0) {
//System.out.println(locale.getCountry() + ":" + isoCountry + ":" + locale.getDisplayName());
byLocale.add(new MyLocale(locale));
}
}
// Roll them out of the set.
ArrayList<String> list = new ArrayList<>();
for (MyLocale l : byLocale) {
list.add(l.getCountry());
}
return list;
}
public static void main(String[] args) throws InterruptedException {
// Some demo usages.
List<String> locales = listAll();
System.out.println(locales);
}
}

yes, when you do sort, just provide your own comparator
Collections.sort(worldCountriesByLocal, new Comparator() {
#Override
public int compare(String o1, String o2) {
if (o1.equals(TOP_VALUE))
return -1;
if (o2.equals(TOP_VALUE))
return 1;
return o1.compareTo(o2);
}
})
where top value will be value what you want to always on top

I would write my own POJO with a sort token consisting of an integer assigning priority (e.g. 0 for US, 1 for UK, 2 for everyone else), then some delimiter and then the country name. Then I would put the array in a HashMap keyed by that sort ID and the POJO as the val. Then I would sort the keys out of the map and iterate through the sorting and retrieve the plain country name for each sorted key.
E.g.
2.Sweden
2.France
2.Tanzania
0.US
1.UK
sorts
0.US
1.UK
2.France
2.Sweden
2.Tanzania
EDIT: a POJO is needed only if you have more fields other than the country name. If it is just the country name, I would set the sort ID as the hash key and the country name as the val and skip the POJO part.

Related

Sort ArrayList of objects with "pinned" objects at top behaves differently each execution

So I am trying to display a list of groups in a recyclerview in Android.
The groups are custom objects (Group) with a small amount of values, stored in a public static Arraylist (allGroups).
I have a method to sort these groups by their "time" value, which is the time in milliseconds.
Method to sort:
public static ArrayList<Group> sort(ArrayList<Group> list) {
list.sort(Comparator.comparing(Group::getTime));
Collections.reverse(list);
ArrayList<Group> newSort = new ArrayList<>();
for(Group g: list) {
if(g.isPinned()) {
newSort.add(g);
}
}
for(Group g: list) {
if(!g.isPinned()) {
newSort.add(g);
}
}
list.clear();
return newSort;
}
When I run the app the first time, it works fine and sorts my groups perfectly by pin and date, but whenever I add a group using the method below, it ONLY sorts it by date
allGroups.add(new Group(
new BigInteger(130, new java.util.Random()).toString(32),
"PB",
(long) (Math.random() * 1649157582577L),
new BigInteger(260, new java.util.Random()).toString(32)
).makePinned(false));
allGroups = sort(allGroups);
groupsAdapter.notifyDataSetChanged();
I have no clue what might be causing this, it makes no sense to me.
Edit:
Implementation for makePinned:
public Group makePinned(boolean pinned) {
this.pinned = pinned;
return this;
}
Constructor of Group:
public Group(String name, String logo, long time, String message) {
this.id = groupAmount + 1;
this.name = name;
this.logo = logo;
this.time = time;
this.message = message;
}
Your "Found the Answer" is incorrect, because your second sort statement :
list.sort(Comparator.comparing(Group::isPinned));
totally overwrites the first sort. OK, for your test sample, it MIGHT be giving the results you desire (coincidentally preserving some of the order from the first sort), but that is undefined behaviour that is NOT to be relied upon.
What it looks like you might be after is better implemented as :
allGroups.sort(Comparator.comparing(Group::isPinned)
.thenComparing(Group::getTime).reversed());
This is explicitly sorting by isPinned first, and then by getTime in reverse order. Explicit is good.
I have written an example program that is available here : Online Java Compiler, that :
sorts as per your question
Randomises the list (ie, undoes the sorting)
Sorts as above

How to implement a sorted table (order by a field of the element) by using java TreeSet?

I used TreeSet for this and it works in a per snapshot style. In other words, sort once displays once.
Now, I want to implement a realtime sorted table.
Whenever there is a value change in any elements, the sorted table will be updated accordingly.
To make the sorting work on a per update style, I tried to remove the element and add it to the TreeSet again.
quotes.remove(quote);
quotes.add(quote);
It doesn't work because I have to implement the sorting logic in compareTo() but it breaks the contract for identifying the object which makes the remove() work. TreeSet never call equals() and hashcode() as described in the Java Doc.
Any idea? Please advise.
code:
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String args[]) {
TreeSetTest test = new TreeSetTest();
test.onQuoteUpdate("appl", 1000d);
test.onQuoteUpdate("msft", 2000d);
test.onQuoteUpdate("face", 3000d);
test.printTopStocks();
test.onQuoteUpdate("msft", 5000d);
test.printTopStocks();
}
private Set<Quote> quotes = new TreeSet<Quote>();
public void onQuoteUpdate(String symbol, double turnover) {
final Quote quote = new Quote(symbol, turnover);
quotes.remove(quote);
quotes.add(quote);
}
public void printTopStocks() {
System.out.println("--Top Stocks By Turnover--");
for (final Quote quote : quotes) {
System.out.println(quote);
}
}
public static class Quote implements Comparable<Quote> {
private String symbol;
private double turnover;
public Quote(String symbol, double turnover) {
this.symbol = symbol;
this.turnover = turnover;
}
#Override
public int compareTo(Quote o) {
return Double.compare(o.turnover, turnover);
// return symbol.compareTo(o.symbol);
}
}
}
Update 1:
As proposed I tried this:
public static void main(String args[]) {
TreeMapTest test = new TreeMapTest();
test.onQuoteUpdate("appl", 1000d);
test.onQuoteUpdate("msft", 2000d);
test.onQuoteUpdate("face", 3000d);
test.printTopStocks();
test.onQuoteUpdate("face", 50d);
test.printTopStocks();
}
public int compareTo(Quote o) {
if(o.symbol.equals(symbol)) return 0;
return Double.compare(o.turnover, turnover);
}
The remove() return false which eventually there are four elements (expected 3) in the Set.
--Top Stocks By Turnover--
Quote [symbol=face, turnover=3000.0]
Quote [symbol=msft, turnover=2000.0]
Quote [symbol=appl, turnover=1000.0]
remove symbol face : false
add symbol face : true
--Top Stocks By Turnover--
Quote [symbol=face, turnover=3000.0]
Quote [symbol=msft, turnover=2000.0]
Quote [symbol=appl, turnover=1000.0]
Quote [symbol=face, turnover=50.0]
Update 2:
I tried PriorityQueue and here is the code:
https://code.sololearn.com/cb38Eo036c8y/#java
It doesn't work because PriorityQueue doesn't store elements in order. The ordering only works when you poll element from the Queue.
Update 3:
Tried user54321's suggestion that by using a custom collection(see below answer). However, it doesn't look good if there are two more elements having the same value of 'turnover'.
My requirement is a very ordinary one. It seems that none of a collection from JDK fits my case.
Update 4:
The solution from user54321 fits for my interim need.
https://code.sololearn.com/c14Ybab7AOFm/#java
Deleted my previously added answer. Looks like a wrong data structure is being used for the scenario.
Here is why.
When an item is being added or removed, TreeSet does a binary search through the available elements using compareTo().
In your case,
After adding first 3 elements, set looks like this.
[{appl, 1000d}, {msft, 2000d}, {face, 3000d}]
Now when you try to remove the element {face, 50d},
It starts searching at {msft, 2000d},
From compareTo() result it determines {face, 50d} should come before {msft, 2000d}.
And continues to search towards start of the elements ( checking with {appl, 1000d} next).
Since the search doesn't find {face, 3000d}, that element remains without being removed.
Next when you add the element {face,50}, similar search happens and since the search does not find {face, 3000},
It adds {face, 50} to the beginning.
Now the set looks like this.
[{face, 50}, {appl, 1000d}, {msft, 2000d}, {face, 3000d}]
Now the problem here is that compareTo() isn't capable of considering both symbol and turnover for a sensible sorting.
TreeSet can be used for getting a sorted collection of unique elements.
If you need to get a sorted collection of different objects with a particular sorting criteria, in this case turnover value, you can use a PriorityQueue
Update: Using a List and a Set in custom data structure
The problem here is that we have to maintain two conditions.
1. Symbol has to be unique
2. Collection should be sorted by turnover value
compareTo() in Quote can check one at a time and not both.
So in this case we may have to go for a custom data structure.
First use only turnover in compareTo();
#Override
public int compareTo(Quote o) {
return Double.compare(o.turnover, turnover);
}
Then implement the custom data structure.
Note that we are using a HashSet to keep track of the symbol alone.
Using a list so that duplicate turnover values can be kept.
static class QuoteCollection {
Set<String> symbols = new HashSet<>();
List<Quote> quotes = new LinkedList<>();
public void onQuoteUpdate(Quote q) {
if (symbols.contains(q.getSymbol())) {
// this requires quotes.equals() to be implemented
quotes.remove(q);
} else {
symbols.add(q.getSymbol());
}
insertToCollection(q);
}
// inserting at correct position to remain sorted
private void insertToCollection(Quote q) {
int index = Collections.binarySearch(quotes, q);
if (index < 0)
index = ~index; // bitwise compliment to find insert position if it is not available in the list
quotes.add(index, q);
}
public List<Quote> getQuotes() {
return quotes;
}
}
Then use it in the main(). Note that printTopStocks() has been changed a little.
public static void main(String args[]) {
Main test = new Main();
QuoteCollection quoteCollection = new QuoteCollection();
quoteCollection.onQuoteUpdate(new Quote("appl", 1000d));
quoteCollection.onQuoteUpdate(new Quote("msft", 2000d));
quoteCollection.onQuoteUpdate(new Quote("face", 3000d));
test.printTopStocks(quoteCollection.getQuotes());
quoteCollection.onQuoteUpdate(new Quote("face", 50d));
test.printTopStocks(quoteCollection.getQuotes());
}
public void printTopStocks(List<Quote> quotes) {
System.out.println("--Top Stocks By Turnover--");
for (final Quote quote : quotes) {
System.out.println(quote);
}
}
This approach does involve data duplication. However a sorted collection can be maintained at linear time complexity(since it uses 'List.remove()')
Couple of points :
Trying to remove elements even when you are adding it first time.
While updating you are trying to remove new element which does not exist in TreeSet. final Quote quote = new Quote(symbol, turnover); here you are building new element which is Quote("face","50d") which does not exist when you are calling quotes.remove(quote);
Below is the one of the way to solve it, I am hard coding oldQuote to keep it short but you can update it:
public void onAdd(String symbol, double turnover) {
final Quote quote = new Quote(symbol, turnover);
quotes.remove(quote);
quotes.add(quote);
}
public void onQuoteUpdate(String symbol, double turnover) {
final Quote newQuote = new Quote(symbol, turnover);
final Quote oldQuote = new Quote("face", 3000d);
quotes.remove(oldQuote);
quotes.add(quote);
}
public static void main(String args[]) {
TreeSetTest test = new TreeSetTest();
test.onAdd("appl", 1000d);
test.onAdd("msft", 2000d);
test.onAdd("face", 3000d);
test.printTopStocks();
test.onQuoteUpdate("face", 50d);
test.printTopStocks();
}

Comparable interface with many conditions

The Question is how can use comparable interface and collections.sort to do the sorting with model , production and price. Can i do these three sorting in ascending order within "public int compareto(car other)"?
For example, It will be sorted with model in alphabetical order. If model is same, then sorted with production in alphabetical order. if production is also same , then finally sorted with price in ascending order.
Thank you for attention, i stuck with this problem many days. Please help me.
public static void main(String[] args) {
ArrayList<Car> car = new ArrayList<car>();
// something ignored//
Collections.sort(car); <----------------------Problem
for (Car c : car) {
System.out.println(c);
}
}
class car implements Comparable<car>{
protected String model;
protected String production;
protected int price;
public Tablet(String model ,String production , int price)
{
this.model=model;
this.price=price;
this.production = production;
}
public int compareTo (car other)
{
?????????????????
}
}
class mini-bus extends car
{
private door;
public Tablet(String model ,String production , int price ,int door)
{
super(model , production , price);
this.door = door;
}
}
The principle is quite straightforward:
Compare the first pair of properties. If they are different, return the negative/positive compare value; otherwise...
Compare the second pair of properties. If they are different, return the negative/positive compare value; otherwise...
... (repeat for as many pairs of properties as you have) ...
Compare the last pair of properties. This is the last property, so return the compare value.
For example:
int compareModels = this.model.compareTo(that.model);
if (compareModels != 0) {
return compareModels;
}
int compareProd = this.production.compareTo(that.production);
if (compareProd != 0) {
return compareProd;
}
return Integer.compare(this.price, that.price);
Note that there is also a nice class in Guava called ComparisonChain which reduces a lot of this boilerplate logic:
return ComparisonChain.start()
.compare(this.model, that.model)
.compare(this.production, that.production)
.compare(this.price, that.price)
.result();
This stops comparing once a difference is found between any pair of properties. It will still access the subsequent properties, but that should hopefully be an irrelevantly cheap thing to do anyway.
Here is the general approach to the problem of multi-attribute sorting:
Decide on the ordered list of attributes by which you sort
For each attribute on your list, compare the values on both sides
If the result is not zero, return it right away
If the result is zero, go to the next attribute on your list
If you ran out of attributes, return zero
If the number of attributes is fixed, the "loop" on the ordered list of attributes is unrolled, i.e. each individual attribute is compared separately:
int res;
res = this.getA().compareTo(other.getA()); // e.g. model
if (res != 0) return res;
res = this.getB().compareTo(other.getB()); // e.g. production
if (res != 0) return res;
res = this.getC().compareTo(other.getC());
if (res != 0) return res;
...
// For the last attribute return the result directly
return this.getZ().compareTo(other.getZ()); // e.g. price
This should do:
public int compareTo(Car other){
if(this.getModel().compareTo(other.getModel()) != 0){
return this.getModel().compareTo(other.getModel());
}else if(this.getProduction().compareTo(other.getProduction()) != 0){
return this.getProduction().compareTo(other.getProduction());
}else{
return Integer.compare(this.getPrice(), other.getPrice());
}
}

Remove Duplicates in an Array List based on element of object

Ok, So I know this topic has been asked lots of times. All usually referring to using a set of some kind.
Now here is the problem.
I have a RateSheet (ArrayList) which has many rates (object) in it. In this rate object we have the variables:
desName
prefix
cost
Now to summarise, I want to take this RateSheet and remove all the duplicate rates based on desName.
I have used a set and collection todo this. Which works, but then I can't go in and access individual objects and then their variables etc as I get cast errors. So I am trying todo this without using sets.
I assume you are creating a set from the rate sheet and inject in that set the desName of each rate sheet.
Unfortunately, the Set api is based upon the equals method and the hashCode methods, so once you imnplemented the equals and hashCode based upon the desName attribute, your sets will contain unique rate sheet objects regarding the desName, if you want them to be unique regarding the prefix, you have to change the equals method and hashCode method.
I've done something like this in the past by plugging in an adapter inside the rate sheet that either links to the desName or to the prefix, hence the hashCode redirects to the "hashCode" provided by the current adapter.
Set<String> s = new HashSet();
for(int i=0;i<RateSheet.size;i++){
Rate rate = (Rate)RateSheet.get(i);
s.add(rate.desName);
}
Set<Rate> rs = new HashSet();
for(int i=0;i<RateSheet.size;i++){
Rate rate = (Rate) RateSheet.get(i);
Iterator it = s.iterator();
while(it.hasNext()){
if(rate.desName.equals(it.next()){
rs.add(rate);
}
}
}
List<RateSheets> dedupSheets(List<RateSheet> sheets) {
Map<String,RateSheet> map = new HashMap<>();
for(Rate r: sheets) map.put(r.getName(), r);
return new ArrayList<>(rateSheets.values());
}
Here's an example code on how to do it with a Set. You need to override hashCode() and equals() because they are used under the hood to find out what's a duplicate and what's not:
import java.util.HashSet;
public class RateSheet
{
private String desName;
public RateSheet(String name)
{
desName = name;
}
#Override
public boolean equals(Object other)
{
if (other == null) return false;
if (other == this) return true;
if (!(other instanceof RateSheet))return false;
RateSheet otherAsRateSheet = (RateSheet)other;
return desName.equals(otherAsRateSheet.desName);
}
#Override
public int hashCode()
{
return desName.hashCode();
}
public static void main(String[] args)
{
HashSet<RateSheet> sheets= new HashSet<RateSheet>();
RateSheet a = new RateSheet("a");
RateSheet b = new RateSheet("a");
RateSheet c = new RateSheet("b");
System.out.println(sheets.add(a));
System.out.println(sheets.add(b));
System.out.println(sheets.add(c));
}
}
This is based on this other answer found here:
Java Set collection - override equals method
and also this one:
Why is equals() not called while adding to HashSet and hashCode matches?
and what #GerritCap posted. (sorry, I am a little slow)

Sorting an ArrayList in Java

So I'm having trouble figuring out how to update a TextArea with information that I submit from an generics arraylist. As of now the program creates a new Order:
Order d1 = new Order();
Then the user selects some data and pushes an add button, and the order is added to a TextArea. The problem I have is that I have to add the order to the correct spot in the list and update it each time. I"m only sorting it by one item. I'm not really sure how to do that using the CompareTo method.
public void actionPerformed(ActionEvent event)
{
ArrayList<Drink> DrinkArray = new ArrayList<Drink>();
if (event.getSource() == addcoffeeButton)
{
String coffeesize = (String) sizecoffeelist.getSelectedItem();
double coffeeprice = Double.parseDouble(pricecoffeeTextfield.getText());
String coffeetype = (String) cuptypecoffeelist.getSelectedItem();
String coffeecaffeine = (String) caffeineList.getSelectedItem();
String coffeeroom = (String) roomforcreamList.getSelectedItem();
String coffeeadditional = additionalflavorList.getText();
if ((coffeeadditional.isEmpty()))
coffeeadditional = "No Additional Flavor";
Drink d1 = new Coffee(coffeesize, coffeeprice, coffeetype, coffeecaffeine, coffeeroom, coffeeadditional);
DrinkArray.add(d1);
orderTextArea.append(d1);
So I would have to add the drink to the correct spot before adding it to the array and printing to the text area, but I'm not quite sure how to do that.
I'll assume that Drink implements Comparable. Look at the javadocs if you don't know what that means.
If that's true, you can do this:
List<Drink> drinks = new ArrayList<Drink>();
// add Drinks
Collections.sort(drinks); // now they're sorted according to your Comparable.
You can also instantiate a Comparator and pass it to the sorts method.
Something like this (make the getValue() function whatever you want):
public class DrinkComparator implements Comparator<Drink> {
public int compare(Drink d1, Drink d2) {
if (d1.getValue() < d2.getValue()) {
return -1;
} else if (d1.getValue() > d2.getValue()) {
return 1;
} else {
return 0;
}
}
public boolean equals(Object obj) {
return this.compare(this, (Drink)obj) == 0;
}
}
You basically need to pre-determine the insertion point where the "object" would be inserted...
Take a look at Collections.binarySearch(List<T>, T)
From the Java Docs
Returns:
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.

Categories

Resources