This is my data set [B,A,D,C,3,10,1,2,11,14]
I want to sort it like this [1,2,3,10,11,14,A,B,C,D]
When i use following
public class ArenaModel implements Comparable<ArenaModel>{
private String sectionName;
#Override
public int compareTo(ArenaModel o) {
return sectionName.compareTo(o.sectionName);
}
In Main class, i do following.
Collections.sort(arenaArrayList);
It does sorting but single digit numbers do not get sort, and i get following result.
[10,11,14,1,2,3,A,B,C,D]
Try this:
public class ArenaModel implements Comparable<ArenaModel>{
private String sectionName;
#Override
public int compareTo(ArenaModel o) {
try {
Integer s1 = Integer.valueOf(sectionName);
Integer s2 = Integer.valueOf(o.sectionName);
return s1.compareTo(s2);
} catch (NumberFormatException e) {
// Not an integer
}
return sectionName.compareTo(o.sectionName);
}
}
Supposing that the String contains no mixed values : characters and digits, you could do that :
import static java.lang.Character.isAlphabetic;
import static java.lang.Integer.parseInt;
#Override
public int compareTo(ArenaModel o) {
String sectionOther = o.getSectionName();
String sectionThis = getSectionName();
// 1) handle comparisons with any alphabetic value
boolean isThisAlphabetic = isAlphabetic(sectionThis);
boolean isOtherAlphabetic = isAlphabetic(sectionOther);
// move the alphabet value at the end
if (isThisAlphabetic && !isOtherAlphabetic){
return 1;
}
// move the alphabet value at the end
if (!isThisAlphabetic && isOtherAlphabetic){
return -1;
}
// compare String values
if (isThisAlphabetic && isOtherAlphabetic){
return sectionThis.compareTo(sectionOther);
}
// 2) By eliminatation, you have two integer values
return Integer.compare(parseInt(sectionThis), parseInt(sectionOther));
}
Related
I have List<Object>. Every Object have property Number (String property).I need to sort the list of objects based on number field Alphanumeric.
public class Object{
private String number;
}
The numbers can be (for example):
#3772-BOZ-007
#3772-BAZ-02
#31-002
#001
Every number starts with #
I already tried and fail with:
lstObjects.sort(Comparator.naturalOrder());
You need to implement Comparable<>.compareTo method .
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Obj implements Comparable<Obj> {
private String number;
public Obj(String number) {
this.number = number;
}
#Override
public int compareTo(Obj o2) {
int i1 = numPart();
int i2 = o2.numPart();
if (i1 == i2)
return number.compareTo(o2.number);
else return i2 - i1;
}
private int numPart() {
int dashPos = number.indexOf('-');
String numPart = number.substring(1,
dashPos == -1 ? number.length() : dashPos);
try {
if (numPart.length() > 0)
return Integer.parseInt(numPart);
} catch (NumberFormatException e) {
//
}
return 0;
}
#Override
public String toString() {
return number;
}
}
Lets Test it:
class Test {
public static void main(String[] args) {
List<Obj> lstObjects = Arrays.asList(
new Obj("#3772-BOZ-007"),
new Obj("#3772-BAZ-02"),
new Obj("#31-002"),
new Obj("#001"),
new Obj("#19"),
new Obj("#22"),
new Obj("#21"),
new Obj("#6"));
lstObjects.sort(Comparator.naturalOrder());
lstObjects.forEach(System.out::println);
}
}
Result:
#001
#6
#19
#21
#22
#31-002
#3772-BAZ-02
#3772-BOZ-007
Object is a term already used by java.lang.Object, thus rather confusing as a sample name. I'll assume you wrote Obj instead for the rest of this answer.
The sort method takes a comparator that compares the things in the list. A comparator is an oracle that returns for any 2 objects which one is 'earlier'.
Your list consists of instances of that Obj class you pasted, which does not have a natural order defined.
You have two options:
Make that class have a natural order
To do so, make the class implement Comparable<Self>. Thus:
public class Obj implements Comparable<Obj> {
private String number;
#Override public int compareTo(Obj other) {
return this.number.compareTo(other.number);
}
}
...
lstObjects.sort(Comparator.naturalOrder()):
Supply a comparator that compares on the number value.
This assumes you have a public String getNumber() method in your Object class:
lstObjects.sort(Comparator.comparing(Obj::getNumber));
You can implement the Comparable interface and define the sort key and mechanism of the sorting in the compareTo() method
example:
public class MyObject implements Comparable<MyObject> {
private String number;
public MyObject(String number) {
this.number = number;
}
#Override
public int compareTo(MyObject other) {
return this.number.compareTo(other.number);
}
}
Let's assume, you have class
package stack.overflow.atest;
public class Flight {
String number;
public Flight(String number) {
this.number = number;
}
#Override
public String toString() {
return "Flight{" + "number=" + number + '}';
}
}
This snippet invocation:
public static void main(String[] args) {
List<Flight> list = Stream.of(
"#3772-BOZ-007",
"#3772-BAZ-02",
"#31-002",
"#001"
).map(Flight::new)
.sorted((f1, f2) -> f1.number.compareTo(f2.number))
.collect(Collectors.toList());
System.out.println(list);
}
results in:
[Flight{number=#001}, Flight{number=#31-002}, Flight{number=#3772-BAZ-02}, Flight{number=#3772-BOZ-007}]
to sort the Objects by the leading #number
Get the specific sort order by changing the HashMap-default of groupingBy to TreeMap with a passed Comparator.
don't confuse Object with java.lang.Object
static Pattern pat = Pattern.compile("(?:#(\\d+))?(.*)?");
ArrayList<Object> list = new ArrayList<>();
Stream.of(
new Object("#3772-BOZ-007"),
new Object("#3772-BAZ-02"),
new Object("#31-002"),
new Object("#001"))
.collect(groupingBy(Object::getNumber,
() -> new TreeMap<String,List<Object>>(new Comparator<String>() {
#Override
public int compare(String num1, String num2) {
Matcher m1 = pat.matcher(num1);
Matcher m2 = pat.matcher(num2);
m1.find();
m2.find();
Integer i1 = m1.group(1) != null ? Integer.parseInt(m1.group(1)) : -1;
Integer i2 = m2.group(1) != null ? Integer.parseInt(m2.group(1)) : -1;
String s1 = m1.groupCount() > 1 ? m1.group(2) : "";
String s2 = m2.groupCount() > 1 ? m2.group(2) : "";
int cmp = i1.compareTo(i2);
return(cmp == 0 ? s1.compareTo(s2) : cmp);
}}), toList())).values()
.forEach(l -> list.addAll(l));
System.out.println(list);
Objects sorted by their #number:
[Object{number=#001},
Object{number=#31-002},
Object{number=#3772-BAZ-02},
Object{number=#3772-BOZ-007}]
I have an initial set of data that can come in two different formats that need to be matched against. The entries will be either an exact match or provide a range. The range values will be integers in decimal format - but the data to match could be hex as well as decimal. The base will be indicated within the initial data. So the initial data will look something like the following (JSON formatted):
[
{"value":"X"},
{"value":"Y"},
{"START":"0000", "END":"0010", "INPUTBASE":"10"},
{"START":"0200", "END":"0300", "INPUTBASE":"16"}
]
So I need a function something like boolean contains(final String value) that would check to see if value is contained in the "keeper" set The strings X, Y, 8, FF would all hit - FF as a hex number (multiple matches are not a concern). What I have done is create two sets one with single values and one with Ranges wrapped to include the base. My code is something like the following:
Set<WrappedRange> wrappedRangeSet;
Set<String> singleEntrySet;
public boolean contains(final String value) {
return singleEntrySet.contains(value) || valueInRange(value);
}
private boolean valueInRange(final String incoming) {
Long value;
for(WrappedRange<Long> wrappedRange : wrappedRangeSet) {
try {
value = Long.valueOf(incoming, wrappedRange.getBase());
} catch (NumberFormatException nfe) {
continue; // not worried if wrong format...
}
if (wrappedRange.contains(value)) {
return true;
}
}
return false;
}
The WrappedRange looks like:
class WrappedRange<C Extends Comparable> {
private final int base;
private final Range<C> range;
public WrappedRange(final Range<C> range, final int base) {
this.range = range;
this.base = base;
}
public boolean contains(final C value) {
return range.contains(value);
}
public int getBase() {
return int base;
}
}
At the minimum I would like to get rid of the WrappedRange.getBase() method and move the conversion from String to the desired type into the WrappedRange class. Then a contains(String) method could be added to WrappedRange class. The loop in the valueInRange method be reduced to:
for(WrappedRange<Long> wrappedRange : wrappedRangeSet) {
if (wrappedRange.contains(value)) {
return true;
}
}
The problem I'm running into is that I've made the assumption the value I am comparing is a Long, and not any extension of Comparable. I could just fix the WrappedRange to use Longs, but then I can't use the class for other Comparables.
Ideally I'd like to restrict the conversion of the Strings to the WrappedRange class, as there is the possibility other extensions of Comparable may be used for input to the contains function in the future. The class would look something like the following:
class WrappedRange<C Extends Comparable> {
private final int base;
private final Range<C> range;
public WrappedRange(final Range<C> range, final int base) {
this.range = range;
this.base = base;
}
public boolean contains(final C value) {
return range.contains(value);
}
public boolean contains(final String incoming) {
C value = convert(incoming);
return range.contains(value);
}
private convert(final String incoming) {
// ... convert to type C and return.
}
}
Is there a simple way to do this? Is it even possible?
If the input will always be a string, and the internal structure will always be a range, why not do something like this?
public abstract class WrappedRange<C extends Comparable> {
private final Range<C> range;
protected WrappedRange(Range<C> range) {
this.range = range;
}
public boolean contains(String incoming) {
try {
C value = convert(incoming);
} catch (Exception ex) {
return false;
}
return range.contains(value);
}
protected abstract C convert(String incoming);
}
class WrappedLongRange extends WrappedRange<Long> {
private final int base;
WrappedLongRange(Range<Long> range, int base) {
super(range);
this.base = base;
}
#Override
protected Long convert(String incoming) {
return Long.valueOf(incoming, base);
}
}
This is my VO
public class SomeVO {
private String name;
private String usageCount;
private String numberofReturns;
private String trendNumber;
private String nonTrendNumber;
private String trendType;
private String auditType;
public SomeVO(String name,String usageCount,String numberofReturns,String trendNumber,String nonTrendNumber,String trendType,String auditType){
this.name = name;
this.usageCount = usageCount;
this.numberofReturns = numberofReturns;
this.trendNumber = trendNumber;
this.nonTrendNumber = nonTrendNumber;
this.trendType = trendType;
this.auditType = auditType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsageCount() {
return usageCount;
}
public void setUsageCount(String usageCount) {
this.usageCount = usageCount;
}
public String getNumberofReturns() {
return numberofReturns;
}
public void setNumberofReturns(String numberofReturns) {
this.numberofReturns = numberofReturns;
}
public String getTrendNumber() {
return trendNumber;
}
public void setTrendNumber(String trendNumber) {
this.trendNumber = trendNumber;
}
public String getNonTrendNumber() {
return nonTrendNumber;
}
public void setNonTrendNumber(String nonTrendNumber) {
this.nonTrendNumber = nonTrendNumber;
}
public String getTrendType() {
return trendType;
}
public void setTrendType(String trendType) {
this.trendType = trendType;
}
public String getAuditType() {
return auditType;
}
public void setAuditType(String auditType) {
this.auditType = auditType;
}
}
Here is my values
List<SomeVO> myList = new ArrayList<SomeVO>();
SomeVO some = new SomeVO("A","0","0","123","123","Trend","AuditX");
myList.add(some);
some = new SomeVO("B","1","1","234","234","Non trend","AuditX");
myList.add(some);
some = new SomeVO("C","0","2","345","345","Trend","AuditX");
myList.add(some);
some = new SomeVO("D","2","3","546","546","Trend","AuditX");
myList.add(some);
some = new SomeVO("E","2","4","678","678","Non trend","AuditX");
myList.add(some);
some = new SomeVO("F","0","0","123","123","Non trend","AuditA");
myList.add(some);
some = new SomeVO("G","0","0","123","123","Trend","AuditB");
myList.add(some);
Here is my comparator
public String currentAudit = "AuditX";
public class AuditComparator implements Comparator<SomeVO> {
#Override
public int compare(SomeVO o1, SomeVO o2) {
if(currentAudit.equalsIgnoreCase(o1.getAuditType()) && currentAudit.equalsIgnoreCase(o2.getAuditType())) {
int value1 = o2.getUsageCount().compareTo(o1.getUsageCount());
if (value1 == 0) {
int value2 = o1.getNumberofReturns().compareTo(o2.getNumberofReturns());
if(o1.getTrendType().equalsIgnoreCase("Trend") && o2.getTrendType().equalsIgnoreCase("Trend")) {
if (value2 == 0) {
return o1.getTrendNumber().compareTo(o2.getTrendNumber());
} else {
return value2;
}
} else {
if (value2 == 0) {
return o1.getNonTrendNumber().compareTo(o2.getNonTrendNumber());
} else {
return value2;
}
}
}
return value1;
} else {
return 1;
}
}
}
I am trying to sort the VO based on below conditions
First only set of values of currentAudit should be taken in to
consideration i.e., AuditX
a) then it should be sorted with
Usage count in descending order
b) if same usage count found then it
should be sorted with Return count in ascending order
c) if same
return count then it should check for trendType, if trendType
="Trend" then it should sort with Trend number otherwise nonTrend number.
then it should consider rest all auditType's and sorted with
a),b),c) condition as like currentAudit. I tried achieving it and i
ended up with only above comparator. Expected result: D, A, C, E,
F, G. But i get G,F,D,E,B,A,C. Please help me to update the
comparator above.
Your comparator does not meet a simple condition: it is not stateless. A following should always be true: A>B => B<A. In your case, in some scenarios A>B and B>A.
I resolved it by splitting the actual list in to 2 list based on AuditX and rest in another list. Then used below comparator one by one, and then merged in to a result list. Works good.
for(SomeVO some:myList) {
if(some.getAuditType().equalsIgnoreCase("AuditX")) {
auditX.add(some);
} else {
auditY.add(some);
}
}
Collections.sort(auditX, new AuditComparator());
Collections.sort(auditY, new AuditComparator());
public class AuditComparator implements Comparator<SomeVO> {
#Override
public int compare(SomeVO o1, SomeVO o2) {
int value1 = o2.getUsageCount().compareTo(o1.getUsageCount());
if (value1 == 0) {
int value2 = o1.getNumberofReturns().compareTo(o2.getNumberofReturns());
if (value2 == 0) {
return (o1.getTrendType().equalsIgnoreCase("Trend") && o2.getTrendType().equalsIgnoreCase("Trend")) ?
o1.getTrendNumber().compareTo(o2.getTrendNumber()):o1.getNonTrendNumber().compareTo(o2.getNonTrendNumber());
} else {
return value2;
}
}
return value1;
}
The return 1 at the bottom of the comparator makes a bug.
The comparator shall only return 1 if the second element is bigger than the first one, but if they're different, you always return 1, so the very first sorting criteria will be messy.
// a helper for case insensitive comparison
private int compareIgnoreCase(String o1,String o2) {
return o1.toLowercase.compareTo(o2.toLowercase());
}
#Override
public int compare(SomeVO o1, SomeVO o2) {
int result=compareIgnoreCase(o1.getAuditType(),o2.getAuditType());
if (result==0) {
// we need to go to the 2nd criteria
result=o2.getUsageCount().compareTo(o1.getUsageCount());
}
if (result==0) {
// ok, 1st and 2nd criteria was the same, go to the 3rd
result=o1.getNumberofReturns().compareTo(o2.getNumberofReturns());
}
if (result==0) {
// check trends
...
}
return result;
}
I found that this representation of multiple comparison criteria makes the code much easier to follow. We first do the highest priority of comparison, and go on with further comparions if the previous comparisons returned that the two elements are the same (i.e. result is still zero).
In case you need to make a descending sorting at some level, simply put a -, e.g.:
result=-o1.something.compareTo(o2.something)
It is a good idea to have only one exit point in a method (this also makes easier to follow what is happening).
I have this enum:
public enum Digits {
ZERO(0);
private final int number;
private Digits(int number) {
this.number = number;
}
public int getValue(){
return number;
}
}
And I would like to make setter in another class which can me offer following feature:
- I will give it integer value (in this case, 0) and that setter will set enum ZERO to my local variable of type Digits
Is that possible?
Many thanks!
It is possible, but not by invoking the enum's constructor, as it's available only within the enum itself.
What you can do is add a static method in your enum that retrieves the correct instance based on a given value, e.g. ZERO if the given value is 0.
Then you'd invoke that method in your other class when given the int argument.
Self contained example
public class Main {
static enum Numbers {
// various instances associated with integers or not
ZERO(0),ONE(1),FORTY_TWO(42), DEFAULT;
// int value
private int value;
// empty constructor for default value
Numbers() {}
// constructor with value
Numbers(int value) {
this.value = value;
}
// getter for value
public int getValue() {
return value;
}
// utility method to retrieve instance by int value
public static Numbers forValue(int value) {
// iterating values
for (Numbers n: values()) {
// matches argument
if (n.getValue() == value) return n;
}
// no match, returning DEFAULT
return DEFAULT;
}
}
public static void main(String[] args) throws Exception {
System.out.println(Numbers.forValue(42));
System.out.println(Numbers.forValue(10));
}
}
Output
FORTY_TWO
DEFAULT
You can do it like this:
private Digits digit;
public void setDigit(int number) {
for (Digits value : Digits.values()) {
if(value.getValue() == number) {
digit = value;
}
}
}
Here is the example how to achieve what you want
public enum Digit {
ONE(1),
TWO(2),
THREE(3);
private static final Map<Integer, Digit> mappingMap = new HashMap<Integer, Digit>();
static {
for (Digit m : Digit.values()) {
mappingMap.put(m.getValue(), m);
}
}
private final int digit;
Digit(int aDigit) {
digit = aDigit;
}
public int getValue() {
return digit;
}
public static Digit getByDigit(int aDigit) {
return mappingMap.get(aDigit);
}
}
This approach has better performance than iterating over all constants for large enums.
I have to use getters and setters for this code and
actually i'm using two classes to get the result
here is Ndc class:
package java4u.com;
public class Ndc {
private String ndcQUAL;
private String ndcCODE;
private String ndcUNIT;
private String ndcQTY;
String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String getndcQUAL() {
if(str.contains("N4"))
{
return "N4";
}
else
{
return "";
}
}
public void setndcQUAL(String getndcQUAL) {
this.ndcQUAL = getndcQUAL;
}
public String getndcCODE() {
if(str.contains("N4")){
int i=str.indexOf("N4");
str=str.substring(i+2,i+13);
return str;
}
else
{
return "";
}
}
public void setndcCODE(String getndcCODE) {
this.ndcCODE = getndcCODE;
}
public String getndcUNIT() {
if(str.contains("N4")) {
str=str.substring(i+13,i+15);
return str;
}else
{
return "";
}
}
public void setndcUNIT(String getndcUNIT) {
this.ndcUNIT = getndcUNIT;
}
public String getndcQTY() {
if(str.contains("N4")) {
do {
int i=str.indexOf(getndcUNIT());
str=str.substring(i,i++);
return str;
} while(str.length()<=35 || str.contains("N4") || str.contains("TPL"));
else
{
return "";
}
}
public void setndcQTY(String getndcQTY) {
this.ndcQTY = getndcQTY;
}
}
here i'm using str variable and the string will be entered during runtime and the condition is if string contains "N4" value then the loop should be continue else return space.
and I have four methods in this program and
getNdcQUAL() method should return "N4" if string contains "N4" value
and getNdcCODE() method should display next 11 digits after the "N4" for this case I shouldn't mention str.substring(2,13)..I should find the position of NdcQUAL and from there to next 11 digits will be print..
and getNdcUNIT() method should display next two bytes qualifier after the 11 digits for this case also I should find the position of NdcCODE and from there to 2 digits
and finally getNdcQTY() method should return the data after the NdcUNIT for this case also I should find the position of NdcUNIT and from there to untill one of the condition is met
here is my main class
package java4u.com;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor.GetterSetterReflection;
public class Test {
public static String getStr(String str)
{
return str;
}
public static void main(String args[]) {
Ndc ndc=new Ndc();
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
try {
System.out.println("enter a string:");
br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
couldn't understand how to pass the string value from Ndc.java to Test.java also couldn't get how to pass other methods from Ndc.java to Test.java
here is the sample output
str=N412345678923UN2345.677
it should return
N4
12345678923
UN
2345.67
please help me!!!!!!
Since you don't have a constructor. You need to manually set the str
If this N412345678923UN2345.677 is the br.readLine(). Then you need to set it in the for your NDC object
String str = br.readLine();
ndc.setStr(str); // now the str is set in your ndc object.
System.out.println(ndc.getndcCODE());
System.out.println(ndc.getndcUNIT());
System.out.println(ndc.getndcCQTY());
You should first pass the string like this :
ndc.setndcQUAL(yourString);
then get the required value :
System.out.print(ndc.getndcQUAL());
Your approach has one major flaw - you need to execute the methods in a predefined order, else it will extract wrong data. You can however use your setStr(String str) method to initialize all proper fields and then just use your getter methods to return the values you've set within your setStr(...) method:
public class Ndc
{
private String ndcQUAL;
private String ndcCODE;
private String ndcUNIT;
private String ndcQTY;
public void setStr(String str)
{
int pos = 0;
if (str.contains("N4"))
{
pos = str.indexOf("N4");
this.ndcQUAL = str.substring(pos, pos+=2);
this.ndcCODE = str.substring(pos, pos+=11);
this.ndcUNIT = str.substring(pos, pos+=2);
String data = str.substring(pos);
// trim the data at the end corresponding to the provided class logic
int p = data.length();
if (data.contains("N4"))
{
p = data.indexOf("N4");
}
else if (data.contains("TLP"))
{
p = data.indexOf("TLP");
}
if (p > 35)
p = 35;
this.ndcQTY = data.substring(0, p);
}
else
this.ndcQUAL = "";
}
public String getndcQUAL()
{
return this.ndcQUAL;
}
public String getndcCODE()
{
return this.ndcCODE;
}
public String getndcUNIT()
{
return this.ndcUNIT;
}
public String getndcQTY()
{
return this.ndcQTY;
}
}
To break the loop if no valid N4 string was entered, you first have to define a kind of loop first and check the getndcQUAL() return value if it equals N4 after you've assigned the input string to setStr(...):
public class Test
{
public static void main(String args[])
{
Ndc ndc=new Ndc();
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
try
{
do
{
System.out.println("enter a string:");
ndc.setStr(br.readLine());
System.out.println("QUAL: "+ndc.getndcQUAL());
System.out.println("CODE: "+ndc.getndcCODE());
System.out.println("UNIT: "+ndc.getndcUNIT());
System.out.println("QTY: "+ndc.getndcQTY());
}
while("N4".equals(ndc.getndcQUAL()));
}
catch (IOException e)
{
e.printStackTrace();
}
}
}