Home of The JavaSpecialists' Newsletter

252New Year Day Spliterator

Posted: 2018-01-01Category: Tips and TricksJava Version: Dr. Heinz M. Kabutz
 

Abstract: We build a simple custom Spliterator to allow us to stream over dates to find how many times the 1st of January was on a Monday.

 

Welcome to the 252nd edition of The Java(tm) Specialists' Newsletter, sent to you from the beautiful sunny Island of Crete and read in over 146 countries around the globe (that I know of). In Greece we say "Kalimera" every morning. Literally translated this means "Good Day", but is only said before noon. "Mera" means day. And every Monday we say "Kali evthomatha", meaning "Good week". Every 1st of the month we say "Kalo mina", meaning, yes, you guessed it "Good month". And of course we have "Kali chronia", which is "Good year" today. These wishes are accumulated, so on a Monday we'd say: "Kalimera ke kali evthomatha". Today is a special day. The 1st of the year falls on a Monday, so I'm going to wish you "Kalimera ke kali evthomatha ke kalo mina ke kali chronia!" (ke means "and" and is written as kai, but pronounced ke).

So when last did we have this date phenomenon? I decided to investigate :-)

Learning.JavaSpecialists.EU: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Year Spliterator

During 2017, we covered lots of interesting topics during our "Heinz's Happy Hour Season 01". One of these was the Spliterator, which we looked at in detail in episode 2. Since then, I've often used the knowledge gained during this episode to create a small custom Spliterator, and use that to make a Stream. I now consider the ability to write a Spliterator as essential knowledge for a professional Java programmer.

I was curious as to how often the 1st of January falls on a Monday. I first thought of writing a simple loop going back in time, but then was wondering whether it wouldn't be better to use the Stream API. So here is a YearSpliterator:

import java.time.*;
import java.util.*;
import java.util.function.*;

/**
 * Takes a start date and iterates backwards one year at a time.
 */
public class YearSpliterator implements Spliterator<LocalDate> {
  private LocalDate date;

  public YearSpliterator(LocalDate startDate) {
    this.date = startDate;
  }

  public long estimateSize() {
    return Long.MAX_VALUE;
  }

  public boolean tryAdvance(Consumer<? super LocalDate> action) {
    Objects.requireNonNull(action);
    action.accept(date);
    date = date.minusYears(1);
    return true;
  }

  public Spliterator<LocalDate> trySplit() {
    return null;
  }

  public int characteristics() {
    return DISTINCT | IMMUTABLE | NONNULL | ORDERED | SORTED;
  }

  public Comparator<? super LocalDate> getComparator() {
    return Comparator.reverseOrder();
  }
}
  

The YearSpliterator is an infinite sized Spliterator, meaning it does not stop once it reaches the Julian calendar or BC. It just keeps on going backwards. The condition of when to stop is the responsibility of whomsoever uses the stream. We thus do not set the SIZED characteristic.

Since we always iterate back by exactly one year, each LocalDate is DISTINCT. The sequence of values returned is always the same and cannot be changed, hence IMMUTABLE. We never get null back, so NONNULL. It is SORTED, by reverse natural order. SORTED implies ORDERED.

Let's have a look at which New Year Days since 1900 had the characteristic of falling on a Monday:

import java.time.*;
import java.util.stream.*;

public class KaliChronia {
  public static void main(String... args) {
    Stream<LocalDate> newYearDays = StreamSupport.stream(
        new YearSpliterator(
            LocalDate.of(2018, Month.JANUARY, 1)), false);
    newYearDays
        .filter(day -> day.getDayOfWeek() == DayOfWeek.MONDAY)
        .takeWhile(day -> day.getYear() >= 1900) // Java 9
        .forEach(System.out::println);
  }
}
  

And here is the output going back to 1900:

2018-01-01
2007-01-01
2001-01-01
1996-01-01
1990-01-01
1979-01-01
1973-01-01
1968-01-01
1962-01-01
1951-01-01
1945-01-01
1940-01-01
1934-01-01
1923-01-01
1917-01-01
1912-01-01
1906-01-01
1900-01-01
  

When I saw 1900-01-01, I thought - oh wow, that would've been a cool date to wish happy new year. But as scientists and engineers, we know of course that 1900-01-01 was not the beginning of the century, but rather 1901-01-01 was. Typical fence-post error! Indeed, the coolest date in the history of modern man was 2001-01-01 and I completely missed it! I lived in South Africa at the time and didn't know the Greek customs. That would have been:

Kalimera, ke kali evthomatha, ke kalo mina, ke kali chronia, ke kali dekaetia (decade), ke kalo eona (century), ke kali chilietiritha (millenium)

I regret having missed that!

I do remember the 1st of January 2007 very well, but not for the reason you might expect. It was our first new years in Crete and with beautiful blue skies (like today) we went for a swim in the sea. The water was a bit cold, but I used to go into the water in 10 degrees Celsius in Cape Town. The problem though wasn't the water temperature, but the wind chill factor when we got out. We all got horrible colds and now I've become a whimp. Once the cicadas start singing, it marks the time to begin swimming :-)

Of course, you could also use this to figure out which days your birthdays were on, for example:

import java.time.*;
import java.util.stream.*;

public class HeinzBirthdayDays {
  public static void main(String... args) {
    StreamSupport.stream(
        new YearSpliterator(
            LocalDate.of(2017, Month.DECEMBER, 4)), false)
        .takeWhile(day -> day.getYear() >= 1971) // Java 9
        .map(day -> day + " -> " + day.getDayOfWeek())
        .forEach(System.out::println);
  }
}
  

If you didn't notice before, Java 9 now has a takeWhile() method, quite useful too. And yep, we discussed it, together with its dangers, in "Heinz's Happy Hour", section 9.17.

Kind regards from Crete

Heinz

 

Related Articles

Browse the Newsletter Archive

About the Author

demo

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

Nobody ever wants to call a Java performance consultant, but with first-hand experience repairing and improving commercial Java applications - JavaSpecialists are a good place to start...

Threading Emergency?

If your system is down, we will review it for 15 minutes and give you our findings for just 1 € without any obligation.