Существует несколько способов решения этой проблемы.Одним из способов является создание самостоятельно отсортированного индекса месяца и года рождения, чтобы вы могли быстро найти следующие дни рождения для данного дня.
Первое, что вам нужно понять, это то, что «следующие» дни рождения не должныпринять во внимание год.Если вы просто отсортируете или сравните дату, год даст вам недействительные даты.Один из способов обойти это - создать ключ для дня рождения, который однозначно идентифицирует день рождения независимо от года.В частности, вы хотите ключ, который можно заказать в «хронологическом порядке».Например:
LocalDate d1 = LocalDate.parse("1980-10-31');
int key1 = d1.getMonthValue() * 100 + d1.getDayOfMonth();
LocalDate d2 = LocalDate.parse("2001-10-31');
int key2 = d2.getMonthValue() * 100 + d2.getDayOfMonth();
key1 == key2
Так что теперь, когда у вас есть способ генерации ключа, вы захотите сгенерировать карту ключей для дней рождения.Вы можете сделать это достаточно легко с помощью сборщика groupingBy.
Function<LocalDate, Integer> key = (d) -> d.getMonthValue() * 100 + d.getDayOfMonth();
Map<Integer, List<LocalDate>> lookup = birthdays.stream().collect(Collectors.groupingBy(key));
Обратите внимание, что мы собираем дни рождения в те же дни, что и список, поскольку может случиться так, что в один и тот же день существует несколько дней рождения., в один и тот же или в разные годы.
Наконец, вы захотите найти следующие три дня рождения.Один из способов сделать это - отсортировать ключи, найти позицию следующей записи и затем прочитать следующие два.Поскольку это упорядоченный список ключей, мы можем создать индекс ключей, отсортировать его и использовать двоичный поиск, чтобы найти следующую запись.Итак, давайте создадим индекс:
List<Integer> searchIndex = lookup.keySet().stream().sorted().collect(Collectors.toList());
Итак, теперь мы можем использовать searchIndex
, чтобы найти ближайший ключ к ключу для даты ввода.Затем мы можем использовать этот ключ, чтобы найти список дней рождения для этого ключа из lookup
.
Мы можем получить следующие две даты, просто взглянув на следующие два ключа в индексе.Обратите внимание, что вы захотите перейти к началу индекса, когда дойдете до конца.Например, если вы будете искать следующие три дня рождения 31 декабря, вам нужно будет продолжить проверку в январе.
List<LocalDate> birthdays = getAllBirthdays();
Function<LocalDate, Integer> key = (d) -> d.getMonthValue() * 100 + d.getDayOfMonth();
Map<Integer, List<LocalDate>> lookup = birthdays.stream().collect(Collectors.groupingBy(key));
List<Integer> searchIndex = lookup.keySet().stream().sorted().collect(Collectors.toList());
int index = Collections.binarySearch(searchIndex, key.apply(LocalDate.now()));
// Find the positive index if the index doesn't contain the current MMdd
if (index < 0) {
index = -index -1;
}
for (int i = 0; i < 3; ++i) {
// Wrap around to the start of the year
if (index >= searchIndex.size()) {
index = 0;
}
System.out.println(lookup.get(searchIndex.get(index++)));
}
Я использовал java.util.LocalDate
в приведенном выше коде, но вы могли бы каклегко использовать joda's LocalDate
.Оба предпочтительнее использовать java.util.Date