I have an empty list. I fill it with my class's instances in a loop. And right after adding an instance, I retrieve the last element and check its parameters. The values of parameters are fine.
Now, when I have filled all the values and control gets out of the loop, the date and time (which are instances of Calendar) of all the elements of that list are somehow replaced with the very last element's date and time, whereas the rest of parameters remain the same. I don't know if there is a logical error in my code or there is a bug in Android Studio.
I am printing out the values to Logcat, before entering the element and after entering that element. The values are same. But when the control reaches cursor.close(), all elements in that list are replaced.
public List<YearChart> readYearChart() throws Exception {
List<YearChart> yearChart = null;
SimpleDateFormat dateFormat = TheApplication.getDateFormat();
SimpleDateFormat timeFormat = TheApplication.getTimeFormat();
Cursor cursor = sqLiteDatabase.query(MyDatabaseHelper.table_YearChart, super.columnsToRetrieve, null, null, null, null, null);
if(cursor.moveToFirst()) {
yearChart = new ArrayList();
int id;
Calendar date = Calendar.getInstance();
int namazId;
Calendar time = Calendar.getInstance();
int i=0;
do
{
id = cursor.getInt(cursor.getColumnIndex(super.columnsToRetrieve[0]));
String dateStr = cursor.getString(cursor.getColumnIndex(super.columnsToRetrieve[1]));
namazId = cursor.getInt(cursor.getColumnIndex(super.columnsToRetrieve[2]));
String timeStr = cursor.getString(cursor.getColumnIndex(super.columnsToRetrieve[3]));
if(!dateStr.isEmpty() && !timeStr.isEmpty())
{
date.setTime(dateFormat.parse(dateStr));
time.setTime(timeFormat.parse(timeStr));
YearChart yc = new YearChart(id, date, namazId, time);
Log.v("YearChart_ID >", String.valueOf(yc.getId()));
Log.v("YearChart_Date>", TheApplication.getDateFormat().format(yc.getDate().getTime()));
Log.v("YearChart_NID >", String.valueOf(yc.getNamazId()));
Log.v("YearChart_Time>", TheApplication.getTimeFormat().format(yc.getTime().getTime()));
Log.v("*****", "*****");
yearChart.add(yc);
YearChart yc1 = yearChart.get(i);
Log.v("YearChart_ID >", String.valueOf(yc1.getId()));
Log.v("YearChart_Date>", TheApplication.getDateFormat().format(yc1.getDate().getTime()));
Log.v("YearChart_NID >", String.valueOf(yc1.getNamazId()));
Log.v("YearChart_Time>", TheApplication.getTimeFormat().format(yc1.getTime().getTime()));
Log.v("*****", "*****");
i++;
}
} while (cursor.moveToNext());
}
cursor.close();
return yearChart;
}
I am using Android Studio (v1.1.0).
You're passing the same reference to date and time - meaning each element in the list holds a reference to those exact objects. When you call date/time methods, you'll be updating those two variables which all your list items point to.
The solution is to move the instantiation of date and time into the do-while loop.
Related
I am having problems with this NumberFormatException Invalid Long. The log cat is showing that the error is coming from the Long.parseLong part of the method.
public static String getDateTimeStr(String p_time_in_millis) {
SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMAT);
Date l_time = new Date(Long.parseLong(p_time_in_millis));
return sdf.format(l_time);
}
Can someone tell me why this code works fine when I fetch and display the data in certain calendars then in other calendars on my device I get this NumberFormatException Invalid Long please?
Edit: Here is the rest of the code…
private void getEvents() {
Uri l_eventUri;
ArrayList<Map<String, String>> allStudents = new ArrayList<Map<String, String>>();
if (Build.VERSION.SDK_INT >= 8) {
l_eventUri = Uri.parse("content://com.android.calendar/events");
} else {
l_eventUri = Uri.parse("content://calendar/events");
}
String[] l_projection = new String[]{"title", "dtstart", "dtend"};
#SuppressWarnings("deprecation")
Cursor l_managedCursor = this.managedQuery(l_eventUri, l_projection, "calendar_id=" + m_selectedCalendarId, null, "dtstart DESC, dtend DESC");
if (l_managedCursor.moveToFirst()) {
int l_colTitle = l_managedCursor.getColumnIndex(l_projection[0]);
int l_colBegin = l_managedCursor.getColumnIndex(l_projection[1]);
int l_colEnd = l_managedCursor.getColumnIndex(l_projection[2]);
String l_title = String.valueOf(l_colTitle);
String l_begin = Integer.toString(l_colBegin);
String l_end = Integer.toString(l_colEnd);
do {
Map<String, String> map = new HashMap<String, String>();
l_title = l_managedCursor.getString(l_colTitle);
l_begin = getDateTimeStr(l_managedCursor.getString(l_colBegin));
l_end = getDateTimeStr(l_managedCursor.getString(l_colEnd));
map.put("eventTitles", l_title);
map.put("event_begin", l_begin);
map.put("event_end", l_end);
allStudents.add(map);
} while (l_managedCursor.moveToNext());
l_managedCursor.close();
SimpleAdapter adapter = new SimpleAdapter(this, allStudents, R.layout.notice_layout, new String[] { "eventTitles", "event_begin", "event_end" }, new int[] { R.id.tvTitle, R.id.tvBody, R.id.tvTeacherCode});
listViewCalendar.setAdapter(adapter);
}
}
Edit 2:
For some reason the code works fine without this line of code so I've nailed it down to this line of code.
l_end = getDateTimeStr(l_managedCursor.getString(l_colEnd));
Why does the l_colEnd get caught up in an NumberFormatExcetion? When the following line of code could be also caught up in the same NumberFormatException because it is enquiring about the same int format?
l_begin = getDateTimeStr(l_managedCursor.getString(l_colBegin));
Thank you too all who have helped. The other interesting thing also is when I add this
int l_cnt = 0;
do {
++l_cnt;
} while (l_managedCursor.moveToNext() && l_cnt < 100);
to the while clause as shown below at the end of the following code the app works fine with no lines of code throwing a NumberFormatException..
if (l_managedCursor.moveToFirst()) {
int l_cnt = 0;
int l_colTitle = l_managedCursor.getColumnIndex(l_projection[0]);
int l_colBegin = l_managedCursor.getColumnIndex(l_projection[1]);
int l_colEnd = l_managedCursor.getColumnIndex(l_projection[2]);
String l_title = String.valueOf(l_colTitle);
String l_begin = Integer.toString(l_colBegin);
String l_end = Integer.toString(l_colEnd);
do {
Map<String, String> map = new HashMap<String, String>();
l_title = l_managedCursor.getString(l_colTitle);
l_begin = getDateTimeStr(l_managedCursor.getString(l_colBegin));
l_end = getDateTimeStr(l_managedCursor.getString(l_colEnd));
map.put("eventTitles", l_title);
map.put("event_begin", l_begin);
map.put("event_end", l_end);
allStudents.add(map);
++l_cnt;
} while (l_managedCursor.moveToNext() && l_cnt < 100);
Can someone tell me why this code works fine when I fetch and display
the data in certain calendars then in other calendars on my device I
get this NumberFormatException Invalid Long please?
Date l_time = new Date(Long.parseLong(p_time_in_millis));
IMO. This code is "bad" code.
Why? You try to fetch unknown data from unchecked sources
"content://com.android.calendar/events" and "content://calendar/events"
Almost everybody can access calendars and save whatever he like... Sadly there is no rule there for using this! So making a wild guess an app is using this event column to save data in another that your expected format.
Regarding the check l_cnt < 100, where fail stops
It's not failing because the error happens to 101 event or later!
My solution would to check my data, and never trust that other apps will act as I expected or as they should.
So I suggest to change getDateTimeStr method as follows:
public static String getDateTimeStr(String p_time_in_millis) {
SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMAT);
long timestamp = 0;
try {
timestamp = Long.parseLong(p_time_in_millis)
} catch(NumberFormatException e) {
Log.w("getDateTimeStr", "Cannot convert '"+p_time_in_millis+"' to long");
e.printStackTrace(); // Prints full error exception
}
Date l_time = new Date(timestamp);
return sdf.format(l_time);
}
Remove the l_cnt < 100 check and leave the code to run checking your logcat! You will now have a better overview of what is happening and also your bad data dates will be 1/1/1970 (due to 0 timestamp) code can be changed responsively in order to handle that dates which does not have the expected format.
Some ideas for handling errors:
Make getDateTimeStr throw an exception to the getEvents() which could catch it and ignore the event with the unexpected data. (Handling logic, ignoring what I cannot understand.)
Recognize the format of the p_time_in_millis in each different case and use different DATE_TIME_FORMAT types regarding the exact format of each event. Okey, that's needs a lot of investigation and still can fail. Also you have to add the case that the format is still unknown so maybe ignore it or use a default value in order not to crash.
Generally, you always try to write a stable app that will not fail if another app saves data in a different format (because it like so or due to its own bug)
I have an array which having time ranges like below,
String[] str ={"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
i want to get the minimum time and maximum time in above array such as "6.10 AM" and "10.00 PM".i can find out using sorting but it takes long time.Is any other method avail.Guide me,Below i sorted like,
String[] str ={"1:0 PM","2:0 AM","3:0 PM",.....};
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm aa", Locale.getDefault());
Date TimeToCompare = null,Time1 = null;
for(int i=0;i<10;i++)
{
TimeToCompare=sdf.parse(str[i]);
for(int j=i+1;j<10;j++)
{
Time1=sdf.parse(str[j]);
if(TimeToCompare.after(Time1))
{
//sorting
}
}
}
This solution makes one pass through the array, keeping track of the min and max times. Runs in O(n).
double maxTime = 0.0;
double minTime = 0.0;
for(String s : str) {
String[] parts = str.split(" ");
double time = Double.parse(parts[0]);
if (parts[1].equals("PM")) {
time += 12;
}
if (time > maxTime) {
maxTime = time;
}
if (time < minTime) {
minTime = time;
}
}
// convert doubles back into strings and print
Date-Time Values
When working with date-time values, it's usually best to work with them as date-time values.
Parse the strings as date-time values, collect them, sort the collection, and retrieve the first and last elements in collection to get earliest & latest values. Convert back to strings if needed.
Joda-Time & java.time
You can easily parse the strings to create date-time objects.
However avoid using the bundled java.util.Date & .Calendar classes in Java as they are notoriously troublesome. Furthermore, they always combine date and time-of-day while in your case you have only a time-of-day.
Use either Joda-Time or the new java.time package in Java 8. Both offer a day-of-time only class, LocalTime.
Example Code
Example code using Joda-Time 2.3.
Convert your array to Collection as I prefer to not work with arrays.
String[] strings = { "6.30 AM", "6.10 AM", "10.00 PM", "7.00 PM" };
List<String> stringList = Arrays.asList( strings );
Create an empty collection to collect our LocalTime objects as we instantiate them.
List<LocalTime> localTimes = new ArrayList<>();
Create a formatter to parse your particular string format. By the way, if you can change the source of these strings, I suggest creating strings in 24-hour format without the "AM/PM", akin to the standard ISO 8601 format.
DateTimeFormatter formatter = DateTimeFormat.forPattern( "h'.'mm aa" );
Loop through our collection of strings, parsing each one. Store the new LocalTime instance in a collection.
for ( String string : stringList ) {
LocalTime localTime = formatter.parseLocalTime( string );
localTimes.add( localTime );
}
Sort the collection of LocalTime objects, to determine the earliest and latest.
Collections.sort( localTimes ); // Ascending order. Earliest first, latest last.
Retrieve the earliest and latest.
LocalTime earliest = localTimes.get( 0 );
LocalTime latest = localTimes.get( localTimes.size() - 1 );
Dump to console.
System.out.println( "localTimes: " + localTimes );
if ( !( localTimes.isEmpty() ) ) {
System.out.println( "earliest: " + formatter.print( earliest ) );
System.out.println( "latest: " + formatter.print( latest ) );
}
When run…
localTimes: [06:10:00.000, 06:30:00.000, 19:00:00.000, 22:00:00.000]
earliest: 6.10 AM
latest: 10.00 PM
Here's a sample solution picked from ggreiner #
How to sort a list of time strings in Java or Groovy
String[] str ={"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
List<String> times = Arrays.asList(str); // convert int to list
Collections.sort(times, new MyComparator()); // use a custom comparator
Log.i("Min time is ",""+times.get(0));
Log.i("Max Time is ",""+times.get(times.size()-1));
Custom Comparator
class MyComparator implements Comparator<String>
{
private DateFormat primaryFormat = new SimpleDateFormat("h.mm a");
#Override
public int compare(String time1, String time2){
return timeInMillis(time1) - timeInMillis(time2);
}
public int timeInMillis(String time){
return timeInMillis(time, primaryFormat);
}
// in milliseconds
private int timeInMillis(String time, DateFormat format) {
Date date = null ;
try {
date = format.parse(time); //
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (int)date.getTime();
}
}
// Try this way,hope this will help you to solve your problem.
String[] str =new String[]{"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
ArrayList<Double> list = new ArrayList<Double>();
HashMap<Double,String> map = new HashMap<Double, String>();
for (int i=0;i<str.length;i++){
list.add(Double.parseDouble(str[i].split(" ")[0]));
map.put(Double.parseDouble(str[i].split(" ")[0]),str[i]);
}
System.out.println("Min >> " +map.get(Collections.min(list)));
System.out.println("Max >> "+map.get(Collections.max(list)));
Can you use these, But you may need some pre-arragments
Collections.max(arrayList);
Collections.min(arrayList);
Depending on how you're originally filling the array of values, like are you getting the long from the system then you could compare those. Or create an class that holds the value part and the AM or PM seperately like a flag or something, so then sort between AM and PM then values. I dabbled a lot with java date and calendars, and just use JodaTime. It's convenient!
I am new to the android world and have a problem with an id. What i need is that when the user clicks on new match it will insert a new row into the db. This is working and i get the lastId but now i need this id in the next activities. How can i store that id so i can use it elsewhere?
This is how i insert the new match:
public void newMatch(WedstrijdenGeschiedenis wedstrijd){
// 1.
SQLiteDatabase db = this.getWritableDatabase();
// 2.
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
Date date = new Date();
ContentValues values = new ContentValues();
values.put(KEY_DATUM, dateFormat.format(date)); // get datum
// 3.
long lastId = db.insert(TABLE_WEDSTRIJD, // table
null, //nullColumnHack
values); // key/value -> keys = column names/ values = column values
Log.d("New Match","ID ="+lastId);
// 4. close
db.close();
}
so i see the lastId in LogCat but i don't know how to store it for further use. I tried void but offcourse that is not possible on void. Sorry for the dummy question
change void to long and add a return statement that returns the lastId
public long newMatch(WedstrijdenGeschiedenis wedstrijd){
// Your other code
return lastId;
}
Access it with:
long lastId= db.newMatch(new WedstrijdenGeschiedenis());
I have read several posts here on speed issues when looping through a cursor and tried the answers given in these posts such as e.g. do not use getcolumnindex in the loop call this once etc.
However with a database having around 2400 records it takes around 3 to 5 minutes to finish.
The loop is running in an async task method so that it does not hang up the device and the database is handled via a database adapter.
The loop code is as follows :
while (!exportrec.isAfterLast()) {
if ( exportrec.moveToNext() ) {
fulldate = exportnumberformatter(exportrec.getInt(daye))
+"/"+exportnumberformatter(exportrec.getInt(monthe))+"/"
+String.valueOf(exportrec.getInt(yeare));
fulltime = exportnumberformatter(exportrec.getInt(houre))+":"
+exportnumberformatter(exportrec.getInt(mine))+":"
+exportnumberformatter(exportrec.getInt(sece));
noiseid = exportrec.getInt(typee);
exportedinfo += exporttypes[id] +","+exportrec.getString(notee)+","+
fulldate+","+fulltime+" \n" ;
}
}
The exportnumberformatter does the following :
public String exportnumberformatter(int i) {
String result = Integer.toString(i);
if (result.length() >1 ) {
return Integer.toString(i);
}
String zeroprefix = "";
zeroprefix = "0"+result;
return zeroprefix ;
}
The cursor is called as follows before the loop to get the data :
exportrec = MD.GetAllLogs(2, "date_sort");
exportrec.moveToFirst();
The MD is the database adapter and the GetAllLogs Method (this has been played with to try and speed things up and so the date_sort that is used is really ignored here):
public Cursor GetAllLogs(Integer i,String sortfield)
{
String sorted = "";
if (i == 1 ) {
sorted = "DESC";
} else if (i == 2) {
sorted = "ASC";
}
return mDB.query(DB_TABLE, new String[] {COL_ID, COL_TYPE,COL_IMAGE, COL_INFO,COL_IMAGE,COL_HOUR,COL_SEC,COL_MIN,COL_DAY,COL_MON,COL_YEAR,COL_SORT_DATE},
null, null, null, null, COL_ID+" "+sorted);
}
When I created the table in the database it had no indexes so I created these via the upgrade method. However they did not error or appear to fail when I did this but what I do not know is A) does the database/table need rebuilding after an index is created and B) how to tell if they have been created ? the two indexes were based on the ID as the first and a field that holds the year month day hour minute second all in on Long Integer.
I am concerned that the loop appears to be taking this long to read through that many records.
Update:
rtsai2000's and the suggestion from CL answer has improved the speed from minutes to seconds
Your exportedInfo String is growing and growing. Save the results in an array and Stringify later (such as with StringBuilder).
You are not closing your cursor after reading the records.
List<String> exportedInfo = new ArrayList<String>();
Cursor exportrec = GetAllLogs();
try {
while (exportrec.moveToNext()) {
String info = String.format("%s, %s, %02d/%02d/%02d, %02d:%02d:%02d",
exporttypes[id],
exportrec.getString(notee),
exportrec.getInt(daye),
exportrec.getInt(monthe),
exportrec.getInt(yeare),
exportrec.getInt(houre),
exportrec.getInt(mine),
exportrec.getInt(sece));
exportedInfo.add(info);
}
} finally {
exportrec.close();
}
return exportedInfo;
I have a small problem. I have a ArrayList listOfSData where every element is some like a date: for example:
[30-03-2012, 28-03-2013, 31-03-2012, 2-04-2012, ...]
Now I was wondering how can I sort this list. I mean I want to sort to this
[28-03-2013, 30-03-2012, 31-03-2012, 2-04-2012, etc].
This list must have String values. How can I sort this list? Help me because I have no idea how can I do that.
You will need to implement a Comparator<String> object that translates your strings to dates before comparing them. A SimpleDateFormat object can be used to perform the conversion.
Something like:
class StringDateComparator implements Comparator<String>
{
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
public int compare(String lhs, String rhs)
{
return dateFormat.parse(lhs).compareTo(dateFormat.parse(rhs));
}
}
Collections.sort(arrayList, new StringDateComparator());
here is a small example based on your input. This could be done with a few lines less, but I thought this would be better to understand. Hope it helps.
List<String> values = new ArrayList<String>();
values.add("30-03-2012");
values.add("28-03-2013");
values.add("31-03-2012");
Collections.sort(values, new Comparator<String>() {
#Override
public int compare(String arg0, String arg1) {
SimpleDateFormat format = new SimpleDateFormat(
"dd-MM-yyyy");
int compareResult = 0;
try {
Date arg0Date = format.parse(arg0);
Date arg1Date = format.parse(arg1);
compareResult = arg0Date.compareTo(arg1Date);
} catch (ParseException e) {
e.printStackTrace();
compareResult = arg0.compareTo(arg1);
}
return compareResult;
}
});
At first already given answers are write, but that decisions they are not very fast.
Standard java Collections.sort use timsort. In average case it takes O(n*log(n)) comparations, so your custom comparator will call O(n*log(n)) times.
If performance is important for you, for example if you have large array you can do following things:
Convert string dates to int or long timestamps. This takes O(n) operation. And then you just sort array of longs or integers. Comparation of two atomic int are MUCH faster than any comparator.
If you want to get MORE speed for this sort, you can use use Radix sort (http://en.wikipedia.org/wiki/Radix_sort). I takes much memory but we can optimize it. As I see you don't need to specify time of day. So the range of values is not very big.
At firts pass (O(n)) you can convert date to integer value, with next assumptions:
1970 01 01 is start date (or more specific time if you know it) and encode like 1
lets max date is 2170 01 01
all month have 31 day. So You get 31*12 = 372 values per year
And than you can just sort array of integers using radix sort. Sorting values with 200 years range takes only 200 * 372 * 4 = 297600 bytes for merge sort array, but you get O(2 * n) complexisty.
Here is a method I wrote for ordering an array of objects by their time parameter. It's done by comparing 2 String times every time, this can be easily adjusted for your date comparison by changing the pattern parameter to: "dd-MM-yyyy"
int repositorySize = tempTasksRepository.size();
int initialRepositorySize = repositorySize;
Task soonTask = tempTasksRepository.get(0);
String pattern = "HH:mm";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
for (int i= 0; i < initialRepositorySize; i++)
{
for (int j= 0; j < repositorySize; j++)
{
Task tempTask = tempTasksRepository.get(j);
try
{
Date taskTime = simpleDateFormat.parse(tempTask.getTime());
Date soonTaskTime = simpleDateFormat.parse(soonTask.getTime());
// Outputs -1 as date1 is before date2
if (taskTime.compareTo(soonTaskTime) == -1)
{
soonTask = tempTask;
}
}
catch (ParseException e)
{
Log.e(TAG, "error while parsing time in time sort: " + e.toString());
}
}
tasksRepository.add(soonTask);
tempTasksRepository.remove(soonTask);
if ( tempTasksRepository.size() > 0 )
{
soonTask = tempTasksRepository.get(0);
}
repositorySize--;
Try to use SimpleDateFormat with "d-MM-yyyy" pattern:
1. Create SimpleDateFormat
2. Parse listOfSData string array to java.util.Date[]
3. Sort date array using Arrays.sort
4. Convert Date[] to string array using the same SimpleDateFormat