Android ArrayList unpredictable behavior - android

I am experiencing a strange issue with ArrayList on Android
If I do this
for(int kk=0;kk<mReadRowIds.size();kk++)
{
if(mRealRowId==mReadRowIds.get(kk))
{
if(kk<mRowNumTimes.size())
{
mArrayNumberPortions.add(mRowNumTimes.get(kk));
bFoundIt=true;
break;
}
else
{
break;
}
}
}
The item is not found, but if I do this
int readrowidforcmp;
for(int kk=0;kk<mReadRowIds.size();kk++)
{
readrowidforcmp = mReadRowIds.get(kk);
if(mRealRowId==readrowidforcmp)
{
if(kk<mRowNumTimes.size())
{
mArrayNumberPortions.add(mRowNumTimes.get(kk));
bFoundIt=true;
break;
}
else
{
break;
}
}
}
The item is found , can someone explain what the difference between these is to me as I have not got a clue. NOTE: Array has to be over 200 items for it to go wrong.

It looks like maybe when you call the ArrayList's get(index) method, it's returning a generic object. In the first example, you're comparing an integer to that generic object, but in the second you're casting the generic object to an integer (by assignment) and then comparing them.

It looks like a typical auto-boxing issue. In your first solution, you wrote "mRealRowId==mReadRowIds.get(kk)". The value in the ArrayList is returned as an Integer and compared to an int auto-cast to an Integer. By comparing the values with == you are performing an identity comparison. The trick is that there is a cache of Integer values between -128 and 127, which is why your code starts breaking around 200.
A simple solution would be to make sure you use only ints and not Integers like in your second solution.

Related

ArrayList Remove Object

I'm working on an app in android studio. The part of the app I'm having issues with is where the user can favourite and remove their favourite item. I do this by adding and removing the item from a list.
The thing is the add functionality works which is:
public void addFavorite(Context context, NewSubject subject) {
List<NewSubject> favorites = getFavorites(context);
if (favorites == null)
favorites = new ArrayList<NewSubject>();
favorites.add(subject);
saveFavorites(context, favorites);
}
I am passing in an object of type "NewSubject" which is just a class of getters and setters for name and id.
The problem arises when I try to remove an item from this list. Code below:
public void removeFavorite(Context context, NewSubject subject) {
ArrayList<NewSubject> favorites = getFavorites(context);
if (favorites != null) {
favorites.remove(subject);
saveFavorites(context, favorites);
}
}
I've even tried something like:
for(int i = 0; i < favorites.size(); i++){
if(favorites.get(i).getSubject_name() == subject.getSubject_name())
favorites.remove(i);
}
Even though both subject names match, the if statement never triggers as true. By changing it to ID it does remove the item but I was wondering why it doesn't work the other way. MeetTitan suggested to use "equals" operator to compare Strings and this has fixed that issue. But I'm still wondering as to why removing the item by "subject" without the FOR loop and IF statement doesn't work.
I have cleared the app's data multiple times whilst trying to debug the source of the problem.
Thank you for your time and help, it is much appreciated.
This applies if you are re-creating NewSubject twice... If you are trying to remove the exact same instance of NewSubject that you got from the collection, then I guessed wrong and this isn't the answer you are looking for.
Is it possible you haven't defined equals and hashCode in your Favorites object? Without those remove will only work with the EXACT same object instance in the collection. If you haven't, try defining them and see if remove() works the way you expect.
Without those methods defined, collections will respond this way:
Obj x=new Obj("data")
Obj y=new Obj("data")
collection.put(x)
collection.remove(y)
assert( collection.size() == 1) // was not removed because .equals didn't exist--remove reverted to == instead which failed, x != y
collection.remove(x)
assert( collection.size() == 0) // NOW it worked because you used the same instance.
if you define .equals and hashCode to compare the strings inside obj, then this will work:
collection.put(x)
collection.remove(y)
assert( collection.size() == 0) // worked because x.equals(y)!
Try
String.equalsIgnoreCase(value1,value2)
This might do your work.
From your example, it's evident that name is a String object. In java, you have to use ".equals()" or comparing two strings.
You can do this:
if(favorites.get(i).getSubject_name().equals(subject.getSubject_name())){
...
}
Or, you can override the equals() method in your NewSubject class to make this work:
favorites.remove(subject);
You can use something like this as your equals() method in the NewSubject class (considering you are only matching two NewSubject objects based on their names):
#Override
public boolean equals(Object other){
if (other == null) return false;
if (other == this) return true;
NewSubject otherSubject = (NewSubject) other;
if(this.getSubject_name().equals(otherSubject.getSubject_name()))
return true;
else
return false;
}
Update:
You may want to override hashcode() as well. If your NewSubject class ever gets used in a hash-based collection such as HashMap, overriding only equals() method will not be sufficient. For reference, this is from Effective Java by Joshua Bloch:
You must override hashCode() in every class that overrides equals().
Failure to do so will result in a violation of the general contract
for Object.hashCode(), which will prevent your class from functioning
properly in conjunction with all hash-based collections, including
HashMap, HashSet, and Hashtable.

How to get object type of preference in Android?

I can get a String from the shared preferences by using:
sharedPreferences.getString("key_name","default value");
But how can I check if key_name is actually a String?
What if it is a Boolean key value?
Is there a method we can use like:
if(sharedPreferences.isTypeOf(Boolean,"key_name")) {}
If you know you will get a boolean you can use
sharedPreferences.getBoolean("key_name",true);
Otherwise you can do (not tested, based on doc)
Map<String, ?> all = sharedPreferences.getAll();
if(all.get("key_name") instanceof String) {
//Do something
}
else if(all.get("key_name") instanceof Boolean) {
//Do something else
}
But you are suppose to know what you stored in your SharedPrefrences
What is expected is you ought to know the data type of your SharedPreference values.
All the shared prefrences that you put are inserted into a Map.
A map cannot have duplicate keys that hold different values. When you use the put operation it basically overwrites the value associated with the key if they key already exists in the map.
You can find how a Map "put" method works here - Java Map
So checking the instanceof for two(or multiple) data types as suggested by #Maloubobola, is kind of absurd since the key can only one value and that value can be of only one data type(which you should know :P).
You can do that but it doesn't make sense like #Blackbelt commented.
All the best :)
If you expect a String, you can also use a try/catch clause:
try {
String strValue = sharedPreferences.getString("key_name","default value")
actionIfString();
} catch (ClassCastException e) {
actionIfNotString();
}

How can I overwrite the data in an element of an array of structures?

So, I have this structure:
typedef struct{
int serialNumber;
char name[100];
float price;
int quantity;
}Products;
And I created an array of structures dynamically.
The task was to 'simulate' a grocery store, with the user able to add and edit the items sold by the store. The following code snippets are for editing structure data.
void overwrite(Products store){
printf("Enter new serial number: ");
scanf("%d", &(store.serialNumber));
getchar();
printf("Enter new product name: ");
fgets(store.name, 100, stdin);
store.name[strlen(store.name)-1]='\0';
printf("Enter new product price: ");
scanf("%f", &(store.price));
printf("Enter new product quantity: ");
scanf("%d", &(store.quantity));
}
void editData(Products *store, int storeCapacity){ //storeCapacity needed to invoke printData(), assume a working code for the function.
int choice;
printData(store, storeCapacity);
printf("Enter slot number of product here: ");
scanf("%d", &choice);
overwrite(store[choice]);
}
Here's the catch, even though this code works, when I try to print the data, the data displays the values which should be overwritten. Have I forgotten to do something? I wish you could help me.
BTW, I code on an Android phone.
void overwrite(Products store){
C is pass by value, you need to pass a pointer to Products (i.e., Products *store) and modify the overwrite call in editData accordingly.
Basically the problem is that in C you pass arguments by value. So when you specify this signature
void overwrite(Products store)
and you invoke it somewhere:
Products myStore;
overwrite(myStore);
what happens is that a copy of myStore is created and placed on the stack, then the value is passed to the function. This means that every modification done to the Products object inside overwrite applies on the passed copy, not on the original object. This copy is then discarded when exiting the scope of overwrite function.
To solve this problem you must pass a pointer to the object, that is passed by value but being an address will point to the exact same myStore object. This is done in this way:
void overwrite(Products* store)
{
..
scanf("%f", &store->price);
..
}
Products myStore;
overwrite(&myStore);
According to Ouah, I passed the structure as the value itself, which did happen in my code.
So what I did is...
void overwrite(Products * store){ //formal parameter changed into a pointer
//codes here
}
And...
overwrite(&(store[choice])); /* actual parameter changed into a pointer by affixing ampersand*/
Further explanations of the codes' misbehavior were explained by Jack. I extend my gratitudes to you. The code now worked as it should be.

Pager with RecyclerViews containing realmObjects keeps crashing with IllegalState

I have a project using Realm.io for storing entities, this used to work fine, but now I have a fragment or activity containing 3 fragments with Lists of Realm objects.
Whenever I Switched to a page and back to the first one (or whatever just returning to a page). I get the java.lang.IllegalStateException: Illegal State: Row/Object is no longer valid to operate on. Was it deleted?
This seems to occur because the objects are no longer valid. Is there a simple way to detach them or something? Allthough it would be nice if they remain managed since I sometimes want to be able to delete them.
The items are queried from database, when there are not sufficient items they will get loaded from the API. Nothing extremely fancy is being used here, three lists with adapters which load the entities. THe difference per list is a string value status, which says if it's an certain status.
I get the error when I load the item from the Adapter after clicking the list item to show the details:
MyEntity myEntity = (MyEntity) adapter.getItem(position);
intent.putExtra("id", myEntity.getId()) <-- this part will crash it.
with exception:
java.lang.IllegalStateException: Illegal State: Row/Object is no longer valid to operate on. Was it deleted?
I guess it's because it's querying the same type of data on three locations (3 tabs). Though I would expect this not to be a problem since they all have their own adapter and list of items. Fetched from their own instances.
This is the code being called by my "Loader" class which handles the from DB and/or Api fetching.
public void loadResult(List result, boolean isFinished) {
//not the best for speed, but at a max of 10 items this is fine to not get duplicates and respect the original order
try {
for (RealmObject ro : result) {
Record r = (Record) ro;
int itemIndex = items.indexOf(r);
if (itemIndex > -1) {
items.set(itemIndex, r);
} else {
items.add(r);
}
}
} catch (IllegalStateException e) {
ErrorClass.log(e);
}
notifyDataSetChanged();
setLoading(!isFinished);
end = result.size() < 10 && isFinished;
}
in short the loader class does this, and it's not a singleton, it's a new instance per Listview (Recycler)
List result = null;
if (sortKey != null) {
result = query.findAllSorted(sortKey, ascending);
} else {
result = query.findAll();
}
if (result.size() < PAGE_SIZE && retry == 0) {
isFinished = false;
retry++;
getPageFromApi(pageNumber);
} else if (retry > 0) {
retry = 0;
}
adapter.loadResult(result, isFinished);
The getPageFromApi will result on this code being called again, and existing entities will be replaced in the list, new items added. So no old removed items should exist in the list when clicking them.
I think this might be very specific but there must be a global reason/solution to my problem.
Stupid me, I wrapped the adding of the new elements in a try catch because of the error before, what was going wrong is pretty simple. In the Loader the items fetched from our API was updating or creating new items. Meaning that those in the list, will be invalid at that point, or at least the pointers to them? Not sure how it works behind the scenes.
What I did to fix it, was loop through all the current items, and check the isValid(), if false the item would be removed. Otherwise I was checking for a new item to be inside the current items List, which would cause the error to occur in the .equals function!
This one thing is something that might be a core error, but I think it's just my error!

Android: Edittext- get current line

In an edittext is there a method for getting the current line of the cursor? If not I will write my own method, but just wanted to check. If I do write my own method would the best method be to go through every character in the edittext until selectionstart and count the number of \n's using a For loop, or is there a better way? Thanks!
Just to let people know:
There is a better way to do this then Doug Paul has suggested by using the getLineForOffset(selection):
public int getCurrentCursorLine(EditText editText)
{
int selectionStart = Selection.getSelectionStart(editText.getText());
Layout layout = editText.getLayout();
if (!(selectionStart == -1)) {
return layout.getLineForOffset(selectionStart);
}
return -1;
}
I can't find a simple way to get this information either, so your approach seems about right. Don't forget to check for the case where getSelectionStart() returns 0. You can make the code reusable by putting it in a static utility method, like this:
private int getCurrentCursorLine(Editable editable) {
int selectionStartPos = Selection.getSelectionStart(editable);
if (selectionStartPos < 0) {
// There is no selection, so return -1 like getSelectionStart() does when there is no seleciton.
return -1;
}
String preSelectionStartText = editable.toString().substring(0, selectionStartPos);
return countOccurrences(preSelectionStartText, '\n');
}
The countOccurrences() method is from this question, but you should use one of the better answers to that question (e.g. StringUtils.countMatches() from commons lang) if feasible.
I have a full working example that demonstrates this method, so let me know if you need more help.
Hope this helps!
find the last index of "\n"using method lastindex=String.lastindexof("\n") then get a substring using method String.substring(lstindex,string.length).and you will get the last line
in two lines of code.

Categories

Resources