1
votes

I have to count the number of times depending on year, month, day, hour, minute.(second is unified to zero, I don't need second)

I chose HashMap as the data structure.

HashMap<Calendar,Integer> arr_time; 

If there are same time(year,month,day,hour,minute) already, I want to increase the Integer, or add a new time(year,month,day,hour,minute).

Calendar calendar = Calendar.getInstance();
calendar.set(mYear,mMonth,mDay,mHour,mMinute,0);
if(arr_time.containsKey(calendar)){
  // increase Integer value
  // ++1;
}else{
  // add new time 
  // arr_time.put(calendar,1);
}

I thought it would recognize the same calendar if year, month, day, hour, and minute were the same. But it was not.

What is the problem?

I didn't use "Date". It's because, Android Devloper said like this.

Date(int year, int month, int date, int hrs, int min, int sec) This constructor was deprecated in API level 1. As of JDK version 1.1, replaced by Calendar.set(year + 1900, month, date, hrs, min, sec) or GregorianCalendar(year + 1900, month, date, hrs, min, sec).

2
I recommend you don’t use Calendar. That class is poorly designed, which is probably causing your trouble, and is also long outdated. Instead use LocalDateTime from java.time, the modern Java date and time API.Ole V.V.

2 Answers

1
votes

Never use Calendar

The terrible Calendar class was supplanted by the java.time classes years ago, specifically ZonedDateTime.

Time zone

You are ignoring the crucial issue of time zone. A date and time-of-day have no real meaning until you provide the context of time zone (or offset-from-UTC). For example, noon is Europe/Paris is much later than noon in Asia/Tokyo and much earlier than noon in America/Montreal.

ZonedDateTime

Represent a date and time-of-day with time zone with the ZonedDateTime class.

ZoneID

Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;

truncatedTo

If you want to set the second and fractional second both to zero, truncate to the minute.

ZonedDateTime zdt = ZonedDateTime.now( z ).truncatedTo( ChronoUnit.MINUTES ) ;  // Set the whole second and the fractional second both to zero.

LocalDateTime

If, for your counting purposes, you want to consider only the date with time-of-day while ignoring the time zone, extract a LocalDateTime. A LocalDateTime is simply a date with time-of-day, and lacks any concept of time zone or offset-from-UTC.

LocalDateTime ldt = zdt.toLocalDateTime() ;

MapSortedMapTreeMap

With a LocalDateTime in hand, you can do your counting. Make a Map where the key is a LocalDateTime, and the value is an Integer.

I imagine you will care about the sorted order of the date-time keys, so use a SortedMap. A TreeMap is one such implementation.

SortedMap< LocalDateTime , Integer > map = new TreeMap() ;

For each LocalDateTime, retrieve an Integer object from the Map. Increment the number count, and replace the old Integer object with a new one.

Using a Map has been covered many hundreds, if not thousands, of times already on Stack Overflow. So search if you need more discussion and examples of that.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

0
votes

java.time

    Map<LocalDateTime, Integer> arr_time = new HashMap<>();
    ZoneId zone = ZoneId.of("Antarctica/Vostok");

    for (int i = 0; i < 10; i++) {
        LocalDateTime now = LocalDateTime.now(zone).truncatedTo(ChronoUnit.MINUTES);
        arr_time.compute(now, (ldt, oldCount) -> oldCount == null ? Integer.valueOf(1) : oldCount + 1);

        TimeUnit.MILLISECONDS.sleep(103);
    }
    System.out.println(arr_time);

When I ran the code just now, I got:

{2019-02-20T13:42=10}

I recorded 10 times, sleeping in between to make sure they were not exactly the same. But because I truncated each to whole minutes, they all ended up being 2019-02-20T13:42 and were counted together.

To create a LocalDateTime from int variables:

    int mYear = 2019;
    int mMonth = Calendar.JANUARY;
    int mDay = 31;
    int mHour = 23;
    int mMinute = 45;
    LocalDateTime ldt = LocalDateTime.of(mYear, mMonth + 1, mDay, mHour, mMinute);
    System.out.println(ldt);

2019-01-31T23:45

Since you used mMonth with Calendar, I assumed it was 0-based. LocalDateTime numbers months from 1, just like humans do, so I needed to add 1.

What went wrong in your code?

calendar.set(mYear,mMonth,mDay,mHour,mMinute,0) sets year, month, day, hour, minute and second but is not setting the milliseconds. Since each Calendar object is created with the current time, the milliseconds will most often be different, so the even though you set the same values, the Calendar objects are still not equal.

That behaviour of the 6-arg set method surprises many and is just a minor point among the many points where the class is poorly designed. You shouldn’t use it. We’ve got java.time since 2014, so there’s really no reason to.

Question: Can I use java.time on Android?

Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links