I am reading in a .csv file sort of like a spreadsheet in excel. There are a certain number of columns, determined by the file, and I read each line into a string array using the .split(",") method. I then put this into an array list so it can hold all of the string arrays without giving it a specific size. However, when I go to sort the array list using Collections.sort(), the program breaks. What could the problem be? Here is my code to sort:
Collections.sort(stringList, new Comparator < String[] > () {
public int compare(String[] strings, String[] otherStrings) {
return -1 * (strings[sortNum].compareTo(otherStrings[sortNum]));
}
});
Two points:
Don't multiply the result of compare by -1 to reverse a comparison. Integer.MIN_VALUE * -1 is still Integer.MIN_VALUE. Instead, reverse the order of the comparison itself
My guess is that you've actually got some rows without enough columns. Perhaps you should put those at the end?
Something like:
Collections.sort(stringList, new Comparator < String[] > () {
public int compare(String[] x1, String[] x2) {
if (x1.length > sortNum && x2.length > sortNum) {
return x2[sortNum].compareTo(x1[sortNum]);
}
if (x1.length > sortNum) {
return 1;
}
if (x2.length > sortNum) {
return -1;
}
return x2.length - x1.length;
}
});
Alternatively, filter your list first to make absolutely sure that all rows have enough columns.
Well, either strings[sortNum] or otherStrings[sortNum] could be out of bounds. You need to do some checks to prevent that. Also, strings[sortNum] or otherStrings[sortNum] could be null. I bet you're running into one of these 2 things. What does the call stack indicate?
Try using this
First your class comparator with a constructor:
public class MyStringArrayComparator implements Comparator<String[]>{
Integer sortNum;
public MyStringComparator(Integer index) {
sortNum = index;
}
#Override
public int compare(String[] strings, String[] otherStrings) {
return -1*(strings[sortNum].compareTo(otherStrings[sortNum]));
}
}
and in your code
Collections.sort(stringList,new MyStringArrayComparator<String[]>(index));
Hope that works for you
Sharing code in case someone need to do the sort on multiple columns.
public final class ArrayComparatorWithIndex<T extends Comparable<T>> implements Comparator<T[]>
{
private final int[] indexToSort;
public ArrayComparatorWithIndex(int[] indexToSort)
{
if(indexToSort == null || indexToSort.length == 0){
throw new IllegalArgumentException("Index to use for sorting cannot be null or empty.");
}
this.indexToSort = indexToSort;
}
#Override
public int compare(T[] str, T[] otherStr)
{
int result= 0;
for (int index : indexToSort)
{
result= str[index].compareTo(otherStr[index]);
if (result != 0){
break;
}
}
return result;
}
}
//Example how to use it:
int[] indexForSorting= new int[] { 1, 3 };
Collections.sort(stringList, new ArrayComparatorWithIndex<String>(indexForSorting));
I suspect you might have a closure problem in reference to the 'sortNum' variable. See Jon Skeet's closure article for some guidance, even though it deals with closures in C# it should still be relevant. Even if you don't have this issue, it's a good read. :)
you can provide default values for empty "cells":
public int compare(String[] strings, String[] otherStrings) {
String one, other;
one = other = ""; // default value
if (sortNum<strings.length && strings[sortNum] != null) {
one = strings[sortNum];
}
if (sortNum<otherStrings.length && otherStrings[sortNum] != null) {
other = otherStrings[sortNum];
}
return -1 * (one.compareTo(other));
}
Related
I have a Object which contains a list of another object which contains a list of another object and so on... suppose I want to get count of nested list elements(lets say last one), what should be best approach rather than using traditional for loop in java as I have done in below example -
public static void main(String[] args) {
Statement statement = new Statement();
statement.getInvAccount().add(new InvestmentAccount());
statement.getInvAccount().get(0).getSecAccountStmt().add(new SecurityStatement());
statement.getInvAccount().get(0).getSecAccountStmt().get(0).getTransactionStatement().add(new TransactionStatement());
statement.getInvAccount().get(0).getSecAccountStmt().get(0).getTransactionStatement().add(new TransactionStatement());
statement.getInvAccount().get(0).getSecAccountStmt().get(0).getTransactionStatement().add(new TransactionStatement());
// method to count the number of TransactionStatement
System.out.println("Size of TransactionStatement is : " + count(statement));
}
private static int count(Statement stmt) {
int countOfTransStmt = 0;
for (InvestmentAccount invAcc : stmt.getInvAccount()) {
if (invAcc != null) {
for (SecurityStatement secStmt : invAcc.getSecAccountStmt()) {
if (secStmt != null) {
countOfTransStmt = countOfTransStmt + secStmt.getTransactionStatement().size();
}
}
}
}
return countOfTransStmt;
}
In Java 7 you're not going to do better than two for loops. I wouldn't bother with anything different.
In Java 8 you can use streams to flatten it out:
private static int count(Statement stmt) {
return stmt.getInvAccount().stream()
.filter(Objects::nonNull)
.flatMap(InvestmentAccount::getSecAccountStmt)
.filter(Objects::nonNull)
.flatMap(SecurityStatement::getTransactionStatement)
.count();
}
I would encourage you to get rid of the null checks. If you're going to ignore nulls, better to just expect them not to be inserted in the first place. It'll get rid of a lot of extra if checks throughout your code, I expect.
I'd also encourage you not to abbreviate your variables and methods. Spell out "statement" and "investment" and the like. The abbreviations are harder to read and the brevity isn't really a win.
Similarly, try to use more descriptive method names. countTransactions is better for the main method. And for the various getters, methods that return lists ought to be plural: "getAccounts" rather than "getAccount". Notice how the getters now match the class names; if you know the class name, you know the getter name. You don't have to guess if one or the other is abbreviated:
private static int countTransactions(Statement statement) {
return statement.getInvestmentAccounts().stream()
.flatMap(InvestmentAccount::getSecurityStatements)
.flatMap(SecurityStatement::getTransactionStatements)
.count();
}
Recursion could work in this case:
General idea below:
private int countTransactions(object t)
{
int sum = 0;
if (t == null) return 0;
for (int i = 0; i < t.getAllSub().count; i++)
{
sum += countTransactions(t.subAt(i));
}
return sum;
}
This question already has answers here:
What's the best way to implement `next` and `previous` on an enum type?
(7 answers)
Closed 6 years ago.
I would like to create a method called increaseValue, which it's signature is as following:
public Size increaseValue(Size s)
Also I have the following statement:
protected enum Size {XS, S, M, L, XL}
I need to know, how can I make the method return correct value (i.e. XL when input is L... etc.) while not using Switch-Case statement ?
Thanks !
You could assume they are in increasing ordinal() order. I would add this a method on Size.
protected enum Size {
XS, S, M, L, XL;
static final Size[] VALUES = values();
public Size incrementSize() { return VALUES[ordinal()+1]; }
public Size decrementSize() { return VALUES[ordinal()-1]; }
}
Note: I wouldn't assume that XS is after XL, but rather you get an error (though not a very clear one)
Note: every time you call values() it creates a new array. It has to do this because the array is mutable and you might change it. I highly recommend saving a copy and avoid calling values() each time.
You can make the error messages clearer by overriding those methods.
protected enum Size {
XS {
public Size decrementSize() { throw new UnsupportedOperationException("No smaller size"); }
},
S,
M,
L,
XL {
public Size incrementSize() { throw new UnsupportedOperationException("No larger size"); }
};
static final Size[] VALUES = values();
public Size incrementSize() { return VALUES[ordinal()+1]; }
public Size decrementSize() { return VALUES[ordinal()-1]; }
}
Here's why you should not do it: if you perform arithmetic on your enum, you can end up with invalid values, for instance what would happen if you added one to XL?
Here's how you do it:
Size.values()[s.ordinal()+1]
In good OO design you want to internalize such things. Meaning: you want to provide a method like "nextSize()" within that enum, like:
public enum Size {
XS, ...;
public Size nextSize() {
switch (this) ...
In this situation, the values are probably "fixed"; but in other situations, you might later want to insert new constants; thus I prefer an explicit mapping here; instead of relying on calls to ordinal().
And as mentioned in the other answers: you need to define what largestSize().nextSize() means. It could throw an exception, or return null (baaad idea). Alternatively, that method could return Optional<Size>; to make it clear to the caller that this method doesn't always return a valid result.
public Size increaseValue(Size s) {
Size[] allValues = Size.values();
var newOrdinal = s.ordinal() + 1;
return (newOrdinal >= allValues.length) ? null) : allValues[newOrdinal];
}
public Size decreaseValue(Size s) {
var newOrdinal = s.ordinal() - 1;
return (newOrdinal < 0) ? null : values()[newOrdinal];
}
You van modify this enum like this:
protected enum Size {
XS,(1)
S(2),
M(3),
L(4),
XL(5);
private int sizeNo;
Size(int sizeNo) {this.sizeNo = sizeNo;}
Size getBySizeNo(int sizeNo){
for(size : Size.values()) {
if (sizeNo == size.getSizeNo() ) {
return size;
}
}
throw new IllegalArgumentException() ;
}
public Size increaseValue(Size s){
return getBySizeNo(s.getSizeNo() +1) ;
}
}
Try this:
public Size increaseValue(Size s) {
return Size.values()[s.ordinal() + 1]
}
I have a list to be sorted but it cannot be done if values are represented as strings. Example:
to sort: OB123, OB1212, Maintenance, Daily check, OB123
desired result: Daily check, Maintenance, OB123, OB123, OB1212
if values are strings result is: Daily check, Maintenance, OB1212, OB123,OB123
Therefore I need to use comparator to first sort aircraft numbers such OB123 by their carrier(OB), than by their number (123) and sometimes suffix (""). And after that I would like to compare the whole name with all the rest values as "daily check" etc.
So far I can sort only flight Ids:
#Override
public int compareTo(FlightNumberDisplay toCompare) {
int result = _carrier.compareTo(toCompare.getCarrier());
if (result == 0) {
result = _number.compareTo(toCompare.getNumber());
if (result == 0) {
result = _suffix.compareTo(toCompare.getSuffix());
}
}
return result;
}
So since "Daily check" has also carrier+number+suffix representation it is sorted according to it. The question is how to sort them according to their names.
Well, you can make a comparison checking for numbers in the strings:
FlightComparator.java
public class FlightComparator implements Comparator<String> {
public int compare(String arg0, String arg1) {
// both have numbers, compare them
if (containsNumber(arg0) && containsNumber(arg0)) {
Integer i1, i2;
try {
i1 = getNumber(arg0);
} catch (NumberFormatException ex) {
return 1;
}
try {
i2 = getNumber(arg1);
} catch (NumberFormatException ex) {
return -1;
}
return i1.compareTo(i2);
} else {
// no numbers
return arg0.compareTo(arg1);
}
}
private boolean containsNumber(String string) {
return string.matches(".*\\d+.*");
}
private Integer getNumber(String string) throws NumberFormatException {
return Integer.parseInt(string.replaceAll("\\D+",""));
}
}
TEST IT
public static void main(String[] args) {
String[] ss = {"OB123", "OB1212", "Maintenance", "Daily check", "OB123"};
Collections.sort(Arrays.asList(ss), new FlightComparator());
list(ss);
}
private static void list(String[] ss) {
for (String s : ss) {
System.out.println(s);
}
}
OUTPUT
Daily check
Maintenance
OB123
OB123
OB1212
DISCLAIMER
Now, while this data seems correct for what you ask, is not a real answer to your problem. Also if flight letters are different ie, OM1212, OR1212, this will only compare the numbers, so to complete solve your problem now, you can choose between
use this Comparator and compare data as shown (not using attributes)
adapt this Comparator<String> to Comparator<Flight> (best option)
CREDITS
Method getNumber created from this answer
Method containsNumber from this answer
You can extract the carrier information in another List, there you can parse it by Integer using parseInt() and then sort it.
Later you can merge both lists.
In my Java program's constructor I have the following:
thirdRow.add(button);
button.setActionCommand("Sumbit");
button.addActionListener(this);
And here is the corresponding actionPerformed method that's supposed to take 3 values from some textfields and store them into arrays:
public void actionPerformed(ActionEvent e)
{
String arg = e.getActionCommand();
if (arg == "Submit")
{
//enlarge arrays
qtyStr = enlargeArray(qtyStr);
typeStr = enlargeArray(typeStr);
colorStr = enlargeArray(colorStr);
//add from textfields into current
qtyStr[qtyStr.length-1] = qty.getText();
typeStr[typeStr.length-1] = type.getText();
colorStr[colorStr.length-1] = color.getText();
}
}
//method to enlarge an array by 1
public String[] enlargeArray(String[] currentArray)
{
String[] newArray = new String[currentArray.length + 1];
for (int i = 0; i<currentArray.length; i++)
newArray[i] = currentArray[i];
return newArray;
}
When I run the application, populate the textfields, and click the submit button nothing happens. How can I verify that my string arrays are being appended like they're supposed to?
You've a problem here: if (arg == "Submit")
Don't compare Strings using ==. Use the equals(...) or the equalsIgnoreCase(...) method instead. Understand that == checks if the two objects are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here. So instead of
if (fu == "bar") {
// do something
}
do,
if ("bar".equals(fu)) {
// do something
}
or,
if ("bar".equalsIgnoreCase(fu)) {
// do something
}
Also, for safety's sake, I try to use String constants wherever possible so as not to be tripped up by misspellings.
If you want to do your code this way, I would probably do two things:
1) maintain index fields for each array for the next free index, and
2) I wouldn't recommend resizing your array by 1 each time, as our current code is running through the array 2 n times (n = array length), 1st to initialize the array, and 2nd to create a new array.
Two options to optimize thisL one would be be to look into the Arrays class. it contains methods such as Array.copyOf() that can perhaps be useful here. You could also check if the array is full, and if it is then resize it by a number greater than one to reduce extra work.
For instance:
import java.util.Arrays;
class Test{
private String[] a;
private int next;
public Test(int size){
a = new String[size];
next = 0;
}
public void add(String s){
if(next == a.length){
Arrays.copyOf(a, a.length+1);
}
a[next] = s;
next++;
}
}
The easiest way would be to use an ArrayList (or any class that implements the java.util.List interface), as previously mentioned by Jon Skeet - it will do all the work for you.
I want to return odd numbers of an array yet Eclipse doesn't seem to accept my return array[i]; code. I think it requires returning a whole array since I set an array as a parameter to my method.
As I said before, I need to pass an array and get a specific element of that array in return. Even if I make that array static, how do I return a single element?
Edit : Alright then, here it is:
public class newClass{
public static void main(String[] args)
{
int [] newArray= new int [4];
int [] array = {4,5,6,7};
newArray[0] = array[0]+array[1]+array[2]+array[3];
newArray[1] = array[0]*array[1]*array[2]*array[3];
newArray[2] = findOut(array);
}
public static int findOut (int [] array3)
{
int e1=0;
int e2=0;
for (int i=0; i<array3.length; i++)
{
if (array3[i]%2==0)
{
e1+=array3[i];
array3[i]=e1
return array3[i];
}
else
{
e2+=array3[i];
array3[i]=e2;
return array3[i];
}
}
}
}
I know there are probably more than a few mistakes here but I'm working on it and I'm not only returning odd numbers, I also add them together.
You code should look like this:
public int getElement(int[] arrayOfInts, int index) {
return arrayOfInts[index];
}
Main points here are method return type, it should match with array elements type and if you are working from main() - this method must be static also.
I want to return odd numbers of an array
If i read that correctly, you want something like this?
List<Integer> getOddNumbers(int[] integers) {
List<Integer> oddNumbers = new ArrayList<Integer>();
for (int i : integers)
if (i % 2 != 0)
oddNumbers.add(i);
return oddNumbers;
}
Make sure return type of you method is same what you want to return.
Eg:
`
public int get(int[] r)
{
return r[0];
}
`
Note : return type is int, not int[], so it is able to return int.
In general, prototype can be
public Type get(Type[] array, int index)
{
return array[index];
}
(Edited.) There are two reasons why it doesn't compile: You're missing a semi-colon at the end of this statement:
array3[i]=e1
Also the findOut method doesn't return any value if the array length is 0. Adding a return 0; at the end of the method will make it compile. I've no idea if that will make it do what you want though, as I've no idea what you want it to do.