5
\$\begingroup\$

In my Android app I store days of week in an int variable by using bit masking.

I'm using the following SparseIntArray to get the day flags for the corresponding days:

public static final SparseIntArray DAY_FLAGS = new SparseIntArray(7);
static {
    DAYS.put(Calendar.MONDAY, 1);
    DAYS.put(Calendar.TUESDAY, 2);
    DAYS.put(Calendar.WEDNESDAY, 4);
    DAYS.put(Calendar.THURSDAY, 8);
    DAYS.put(Calendar.FRIDAY, 16);
    DAYS.put(Calendar.SATURDAY, 32);
    DAYS.put(Calendar.SUNDAY, 64);
}

I'd like to be able to create String from a mask, for example:

int value - String


1 - "Mon"

42 - "Tue Thu Sat"

127 - "Mon Tue Wed Thu Fri Sat Sun"

For this I'm using the following method:

public static String maskToString(Context context, int mask, int firstDayOfWeek) {
    List<Integer> dayList = new ArrayList<>(7);

    // The idea is to iterate through the SparseIntArray and check
    // if the bit in the mask is set for the particular day.
    // If it is, add that day to the dayList.
    for (int i = 0; i < DAY_FLAGS.size(); i++) {
        if (isFlagSet(DAY_FLAGS.valueAt(i), mask)) {
            dayList.add(DAY_FLAGS.keyAt(i));
        }
    }

    // Sort the days by taking the first day of the week into account
    Collections.sort(dayList, new DayComparator(firstDayOfWeek));

    // This List will contain the individual Strings for the days
    List<String> dayStringList = new ArrayList<>(dayList.size());

    // dayToString() simply returns a String resource for a given day
    for (Integer day : dayList) {
        dayStringList.add(dayToString(context, day));
    }

    // join the individual Strings into a single String
    return TextUtils.join(" ", dayStringList);
}

The isFlagSet() method simply checks if a given flag is set in a given mask:

private static boolean isFlagSet(int flag, int mask) {
    return ((mask & flag) == flag);
}

My maskToString() method seems clumsy (mostly in terms of unnecessary(?) object creation). I believe it can be optimized, I just don't know how.

(I can use only Java 7 features, no Java 8)

I'd appreciate any advice.

EDIT:

One thing I came up with regarding optimization is to create a static final DayComparator instance and pass the firstDayOfWeek variable in a setter (instead of the constructor):

private static final DayComparator DAY_COMPARATOR = new DayComparator();

public static String maskToString(Context context, int mask, int firstDayOfWeek) {
    List<Integer> dayList = new ArrayList<>(7);

    for (int i = 0; i < DAY_FLAGS.size(); i++) {
        if (isFlagSet(DAY_FLAGS.valueAt(i), mask)) {
            dayList.add(DAY_FLAGS.keyAt(i));
        }
    }

    // so we do not create a new object here every time the method is called
    DAY_COMPARATOR.setFirstDayOfWeek(firstDayOfWeek);
    Collections.sort(days, DAY_COMPARATOR);

    List<String> dayStringList = new ArrayList<>(dayList.size());

    for (Integer day : dayList) {
        dayStringList.add(dayToString(context, day));
    }

    return TextUtils.join(" ", dayStringList);
}

What do you think? Any other suggestions?

\$\endgroup\$
3
\$\begingroup\$

Current approach

You can determine the exact count of days in the mask beforehand -> you can use a better initial size than 7 for the array list (or directly an array as the size is fixed).
You don't have to sort the list (and thus don't need the comparator), rotating based on the firstDayOfTheWeek is sufficient as the order of the days does not change.
While this is very ugly: You could replace the new ArrayList<>(dayList.size()) with (List<String>)(List<?>)dayList.

Regarding your edit: This seems like a very bad idea if you ever want to make your app multithreaded.

Alternative implementation

You can avoid rotating the resulting list if you start interpreting the mask at the provided firstDayOfWeek instead of Monday.

public static final int MONDAY    = 0x01;
public static final int TUESDAY   = 0x02;
public static final int WEDNESDAY = 0x04;
public static final int THURSDAY  = 0x08;
public static final int FRIDAY    = 0x10;
public static final int SATURDAY  = 0x20;
public static final int SUNDAY    = 0x40;

public static String maskToString(int mask, int firstDayOfWeek) {
    assert (mask & ~127 | firstDayOfWeek & ~127
            | firstDayOfWeek & firstDayOfWeek - 1) == 0;
    String[] values = new String[bitCount(mask)];
    int i = 0, d = firstDayOfWeek;
    do {
        if ((mask & d) != 0)
            values[i++] = dayToString(d);
    } while ((d = d << 1 & 127 | d >> 6) != firstDayOfWeek);
    return Strings.join(" ", values, true);
}

Which gives the following result:

maskToString(MONDAY, MONDAY);                              // Mon
maskToString(TUESDAY | THURSDAY | SATURDAY, MONDAY);       // Tue Thu Sat
maskToString(TUESDAY | THURSDAY | SATURDAY, SUNDAY);       // Tue Thu Sat
maskToString(TUESDAY | THURSDAY | SUNDAY, MONDAY);         // Tue Thu Sun
maskToString(TUESDAY | THURSDAY | SUNDAY, SUNDAY);         // Sun Tue Thu
maskToString(127, MONDAY);      // Mon Tue Wed Thu Fri Sat Sun
maskToString(127, SUNDAY);      // Sun Mon Tue Wed Thu Fri Sat
maskToString(127, WEDNESDAY);   // Wed Thu Fri Sat Sun Mon Tue
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.