Android ArrayList Overwriting Values Instead of Appending to List - java

I'm attempting to update and ArrayList with new data - however when I attempt to do so - the previous data is overwritten and the new data is never appended to the list and I'm unsure why.
if (keylist.contains(item) && !value.equals("start") && !value.equals("stop") && !value.equals("done")){
final Integer number = keylist.indexOf(item);
this.runOnUiThread(new Runnable() {
#Override
public void run() {
// GET PREVIOUS VALUES
String previous_key = App.sharedPrefs.getString(LIVE_VIEW_KEY, null);
String previous_value = App.sharedPrefs.getString(LIVE_VIEW_VALUE, null);
// ADD NEW AND OLD VALUES TO LIST
mCallElements.clear();
mCallElements.add(new CallElement(previous_key, previous_value));
mCallElements.add(new CallElement(valuelist.get(number), value));
mAdapter.notifyDataSetChanged();
// SAVE NEW VALUES
final SharedPreferences.Editor editor = App.sharedPrefs.edit();
editor.putString(LIVE_VIEW_KEY, valuelist.get(number));
editor.putString(LIVE_VIEW_VALUE, value);
editor.apply();
}
});
}
P.S.
This is part of a Twilio chat implementation - full source can be seen here: https://pastebin.com/imNQdCJ0

You're clearing the ArrayList with .clear() and then calling .notifyDataSetChanged(). This clears the last set of data, and then forces the Adapter to completely reload.
To make your life as a developer significantly easier, I would recommend using DiffUtil. This calculates the difference between two lists, and deals with the changes for you. A really good tutorial on how to implement this can be found on CodePath: https://guides.codepath.com/android/using-the-recyclerview#diffing-larger-changes
The Android Documentation says:
'rely on notifyDataSetChanged() as a last resort'
But because of DiffUtil you don't have to care anymore.

I think if you just want to add new data to the old data,you do not need to use mCallElements.clear(),if the notifydatachanged still doesn't update the data, I think you need to check if your ArrayList is the same object.I hope my answer will help you

Related

HashSet reorders the info, what can I do to not be reorded?

I'm having doubt and a problem I tried to solve with this Note App I found on Google, which is pretty simple, so I (a beginner) went to try few things on it.
Everytime I save few notes, close app, restart app, it reorganizes the notes alphabetically, which I don't want. I know that Set and ArrayList are different in the sense a Set won't repeat elements, but ArrayList will. Also Set can't guarantee the order when called.
The question is: how is a good way to solve this instrinsecally sorting problem?
I've tried to switch what's HashSet to ArrayList, but the method putStringSet requires a Set in its parameters, which ArrayList isn't.
The following code works (doesn't crash or any other problem) but doesn't work as I want.
I have this part code in the MainActivity
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("com.tanay.thunderbird.deathnote", Context.MODE_PRIVATE);
HashSet<String> set = (HashSet<String>) sharedPreferences.getStringSet("notes", null);
// a lot of other things here
if (set == null) {
notes.add("Example Note");
} else {
notes = new ArrayList<>(set); // to bring all the already stored data in the set to the notes ArrayList
}
On NoteEditorActivity I have
Intent intent = getIntent();
noteID = intent.getIntExtra("noteID", -1);
if (noteID != -1) {
editText.setText(MainActivity.notes.get(noteID));
} else {
MainActivity.notes.add(""); // as initially, the note is empty
noteID = MainActivity.notes.size() - 1;
MainActivity.arrayAdapter.notifyDataSetChanged();
}
//other things here
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
MainActivity.notes.set(noteID, String.valueOf(s));
MainActivity.arrayAdapter.notifyDataSetChanged();
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("com.tanay.thunderbird.deathnote", Context.MODE_PRIVATE);
HashSet<String> set = new HashSet<>(MainActivity.notes);
sharedPreferences.edit().putStringSet("notes", set).apply();
}
If you would like to keep the order of the notes, Array or List is truly the better choice than using Set.
To store this into SharedPreferences, you can refer to Put and get String array from shared preferences
This question has answers explaining the followings:
Jsonify the list and parse it back.
Concatenate all the Strings with delimiter and split it back.

Java List being modified from somewhere despite being of Collections.unmodifiableList()

I have three lists in my program.
ONE A global list of all items. ALL
TWO A list showing all items that are available to the user. VISIBLE
THREE A list of all items currently visible on the screen. SHOWING
The lists were declared as so:
ONE = external source
TWO = ONE
THREE = ONE
I have this issue when responding to the user filtering specific items. The function will iterate over THREE and List.remove(index) items that match the filtered item. The function does not interact with TWO. Despite this TWO is modified to be equivalent to THREE despite the initial declarations not being run again (I checked with the debugger and can't work out when TWO is ever modified).
I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?
I fixed the issue with some serious jankiness.
Object[] temp = new Object[ONE.size()];
temp=ONE.toArray(temp);
TWO = Arrays.asList(temp);
Awful I know... It works because Arrays.asList is immutable but surely the unmodifiable list is also immutable?
Added Code for those requesting.
Initialisation Code:
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
SpaceObject[] atom = new SpaceObject[visibleObjects.size()];
atom=visibleObjects.toArray(atom);
VISIBLE_OBJECTS = Arrays.asList(atom);
SHOWING_OBJECTS = visibleObjects;
addToView(visibleObjects);
}
Responding to removing or adding items.
private void showSpecificItems(String itemType, Boolean remove){
int i = 0;
if (remove) {
while (i < SHOWING_OBJECTS.size()){
if(SHOWING_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
SHOWING_OBJECTS.remove(i);
}else{
i++;
}
}
}else{
System.out.println("remove");
while(i < VISIBLE_OBJECTS.size()){
if(VISIBLE_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
SHOWING_OBJECTS.add(VISIBLE_OBJECTS.get(i));
}
i++;
}
SHOWING_OBJECTS = sortBrightest(SHOWING_OBJECTS);
}
addToView(SHOWING_OBJECTS);
}
If you have code like this
List<Object> ONE = someMagic();
List<Object> TWO = ONE;
List<Object> THREE = ONE;
Then you have three things all pointing at the same list.
If you want them to be different, you could try something like this:
List<Object> ONE = someMagic();
List<Object> TWO = ONE.clone();
List<Object> THREE = ONE.clone();
This way they make actual different lists.
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
// this line is useless - you allocate an array only to immediately toss it away?!
SpaceObject[] atom = new SpaceObject[visibleObjects.size()];
atom=visibleObjects.toArray(atom);
// this uses atom as its backing array, which comes from visibleObjects
VISIBLE_OBJECTS = Arrays.asList(atom);
// this obviously atom as a backing array
SHOWING_OBJECTS = visibleObjects;
addToView(visibleObjects);
}
So to fix this, you need to remove the dependency on that backing array
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
VISIBLE_OBJECTS = visibleObjects.clone();
SHOWING_OBJECTS = visibleObjects.clone();
addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}
if clone is not available for you, you can do it a little more complicated
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
VISIBLE_OBJECTS = new ArrayList(visibleObjects);
SHOWING_OBJECTS = new ArrayList(visibleObjects);
addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}
I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?
The javadocs say this about unmodifiable views as created by that method:
An unmodifiable view collection is a collection that is unmodifiable and that is also a view onto a backing collection. Its mutator methods throw UnsupportedOperationException, as described above, while reading and querying methods are delegated to the backing collection. The effect is to provide read-only access to the backing collection. ...
Note that changes to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view. Thus, an unmodifiable view collection is not necessarily immutable.
In short, unmodifiableList() does not have the properties that you expect. If you want an immutable snapshot of a mutable list, you need to copy it.

Using past data with present data of ArrayList inside Hashmap in Java

Continuous data from three sensors (x,y, and z) are stored in a Hashmap (sensor names as Key and data in ArrayList as value). For data from every sensor, I need to apply a low pass filter. I'm trying to do it in the following way:
ArrayList<Float> TempArray = new ArrayList();
Map<String,ArrayList<Float>> FilteredDataOfSensors = new HashMap<String, ArrayList<Float>>();
float filteredData;
for(Map.Entry<String , ArrayList<Float>>Sensors:ContinuousDataFromSensors.entrySet())
{
TempArray = AccessPointId.getValue();
String SensorName = Sensors.getKey();
ArrayList<Float> arrayOfFilteredData = FilteredDataOfSensors.get(SensorName);
if(arrayOfFilteredData == null)
{
arrayOfFilteredData = new ArrayList<Float>();
FilteredDataOfSensors.put(SensorName,arrayOfFilteredData);
}
if(arrayOfFilteredData.size() == 0 && TempArray.size() == 1) {filteredData = TempArray.get(0); Log.d("Test", "Inside If");}
else if(arrayOfFilteredData.size() == 0 && TempArray.size() > 1) {filteredData = TempArray.get(TempArray.size()-1);Log.d("Test", "Inside else If");}
else { FilteredData = (float) (0.5 * TempArray.get(TempArray.size()-1) + 0.5 * arrayOfFilteredData.get(arrayOfFilteredData.size()-1)); Log.d("Test", "Inside else");}
arrayOfFilteredData.add( FilteredData);
}
While running the above code snippet, the loop never goes inside the else condition.
In my case, when the app starts, it may not get data from all the sensors at the beginning. The above code snippet starts when there is at least one data from each sensor. So, when the code starts, there may be many data from a sensor and only one data from another sensor. I need to filter the sensor data using the past data and current data.
The hashmap ContinuousDataFromSensors contains the raw data (Key: sensor name, Value: continuous sensor data in ArrayList)
Is my approach correct?
Thank you.
The way FilteredDataOfSensors is created before the loop runs might be your issue. Perhaps it needs to be promoted to a class member and persisted and reused for multiple runs of the loop.
Because the else code you ask about only runs when a Sensor.getKey() already exists in FilteredDataOfSensors. But FilteredDataOfSensors was just created so it is always empty when the loop begins.
Perhaps if ContinuousDataFromSensors which the code loops on had multiple entries for a given Sensor.getKey(). But that seems unlikely, since most maps use unique keys. And also since multiple data points for a sensor do not go in multiple map entries, they are all in the ArrayList of one map entry)
Thanks for putting a code snippet with your question btw. Putting the complete method or class makes it even easier to help. Good luck!

Order a ParseQuery by the date at which only one field has been updated

I have a feed containing posts that are currently ordered by "updatedAt". My original intention was to push up posts to the top of the feed which were last replied to (I do this by incrementing a "replyCount" field in each post when a user leaves a comment), not totally cognizant of the fact that another field, "likeCount" is also being updated when user's "like" a post. I would still like to push those posts that were recently "replied to" to the top of the feed, but do not at the expense of the weird UX behavior that associated with liking posts pushing them up as well. I'm not sure how to separate the two.
What should I do here? Can I maybe add another column called "lastReplyCountTime" and then sort queries based on that? Maybe set lastReplyCountTime for all posts to the current time when saved to the database, and then only update that value when a post receives a reply?
String groupId = ParseUser.getCurrentUser().getString("groupId");
ParseQuery<ParseObject> query = new ParseQuery<>(ParseConstants.CLASS_POST);
query.whereContains(ParseConstants.KEY_GROUP_ID, groupId);
/*query.addDescendingOrder(ParseConstants.KEY_CREATED_AT);*/
query.orderByDescending("updatedAt");
query.findInBackground((posts, e) -> {
if (mSwipeRefreshLayout.isRefreshing()) {
mSwipeRefreshLayout.setRefreshing(false);
}
if (e == null) {
// We found messages!
mPosts = posts;
String[] usernames;
usernames = new String[mPosts.size()];
int i = 0;
for(ParseObject post : mPosts) {
usernames[i] = yeet.getString(ParseConstants.KEY_SENDER_NAME);
i++;
}
FeedAdapter adapter = new FeedAdapter(
getListView().getContext(),
mYeets);
setListAdapter(adapter);
}
});
}
You have 2 options:
Like you suggested you can create another date property let's call it sortedUpdatedAt and update it with the current date each time you are updating the relevant values
If you still want to use updatedAt you can wrap your like object in a separate parse object (class). This class will be saved as a relation in the parent class and then each time the user "like" you can just update only this class and not the whole object. This way the updatedAt of your parent object will not be changed.
I think that option 1 is great since it's not so complicated and you can do it very quick.

binary search on android.support.v7.util.SortedList

I am using android.support.v7.util.SortedList to bind data on RecyclerView. Everything works fine except that when I tried updating data, indexOf(obj) always returns -1.
private int findItem(ZoneContactLoaderAdapter mAdapter, ContactModel contactModel) {
// int index = Collections.binarySearch(mAdapter.getContactModel(), contactModel, new BinarySearchWithComparator());
return mAdapter.getContactModel().indexOf(contactModel);
}
The logic is like this :
int position = findItem(zoneContactLoaderAdapter, notificationContactModel);
if (position < 0) {
ContactModel adapterContactModel = new ContactModel(conversationID, Constants.ACCOUNT_TYPE, conversationName, "", null, conversationName, conversationID, false);
updateAdapterModel(adapterContactModel, notificationContactModel);
zoneContactLoaderAdapter.getContactModel().add(adapterContactModel);
} else {
notificationContactModel.setIsConversation(true);
ContactModel adapterContactModel = zoneContactLoaderAdapter.getItem(position);
updateAdapterModel(adapterContactModel, notificationContactModel);
zoneContactLoaderAdapter.getContactModel().updateItemAt(position, adapterContactModel);
}
The position always returns -1 and duplicate the data.
Tried out solution from this thread , it still doesn't work fine. So, I thought of implementing custom binary search on the list itself using this as an example. But collection only takes List.
What better way can I find index of item on android.support.v7.util.SortedList. Thanks
SortedList's indexOf method is very much dependent on the contained object's proper implementation of the hashCode() and equals() methods.
Leaving the default Object implementations is pretty much guaranteed to not work in your situation.
You can implement them yourself, you can choose to have Android Studio generate the methods for you, or you can use Project Lombok's #EqualsAndHashCode annotation to generate them at compile time. There are probably other options out there too - these are the ones that come to mind first.

Categories