Running on Java 22-ea+27-2262 (Preview)
Home of The JavaSpecialists' Newsletter

252New Year Day Spliterator

Author: Dr. Heinz M. KabutzDate: 2018-01-01Java Version: 8Category: Tips and Tricks
 

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 :-)

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

New Year Day 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

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

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

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

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

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...