Tengo que escribir un método que toma una SortedSet<MyEvent>
y una List<String>
. Tiene para determinar si hay una secuencia sucesiva de MyEvent
que representa el dado List<String>
por un determinado atributo de clase.
Vamos a suponer que no era la situación siguiente (código-):
List<String> list = new ArrayList<String>()
list.add("AAA");
list.add("BBB");
y una Set<MyEvent>
SortedSet<MyEvent> events = new TreeSet<MyEvent>();
con objetos del tipo MyEvent
que implements Comparable<MyEvent>
(comparación por LocalDateTime
solamente).
Lo dado List<String>
representa una secuencia de abreviaturas y necesito encontrar la más reciente aparición de una secuencia de MyEvent
s cuyos atributos de clase abbreviation
que los valores de la secuencia.
Esto es lo que he hecho hasta ahora:
public static void main(String[] args) {
SortedSet<MyEvent> events = generateSomeElements();
List<String> sequence = new ArrayList<>();
sequence.add("AAA");
sequence.add("BBB");
MyEvent desired = getMostRecentLastEventOfSequence(events, sequence);
System.out.println("Result: " + desired.toString());
}
public static MyEvent getMostRecentLastEventOfSequence(SortedSet<MyEvent> events,
List<String> sequence) {
// "convert" the events to a List in order to be able to access indexes
List<MyEvent> myEvents = new ArrayList<MyEvent>();
events.forEach(event -> myEvents.add(event));
// provide a temporary data structure for possible results
SortedSet<MyEvent> possibleReturnValues = new TreeSet<MyEvent>();
// iterate the events in order to find those with a specified predecessor
for (int i = 0; i < myEvents.size(); i++) {
if (i > 0) {
// consider only successive elements
MyEvent a = myEvents.get(i - 1);
MyEvent b = myEvents.get(i);
// check if there is a
if (a.getAbbreviation().equals(sequence.get(0))
&& b.getAbbreviation().equals(sequence.get(1))) {
// if a sequence was found, add the last element to the possible results
possibleReturnValues.add(b);
}
}
}
// check if there were possible results
if (possibleReturnValues.size() == 0) {
return null;
} else {
// if there are any, return the most recent / latest one
return possibleReturnValues.stream().max(MyEvent::compareTo).orElse(null);
}
}
El método está trabajando (para esta secuencia de 2 elementos, al menos).
¿Es posible hacer eso en una sola llamada usando la API de corriente (y por un tamaño desconocido de la secuencia)?
Su tarea no es tan difícil, basta con crear una Stream
, aplique una filter
, y pregunte por el valor máximo. No es el obstáculo que es necesario un elemento anterior en el predicado, pero tenemos las manos en la colección de origen, que puede proporcionarla.
En la práctica, cada SortedSet
es también una NavigableSet
que proporciona un lower
método para obtener el elemento anterior, si es que existe, pero desde su requerimiento es para apoyar una SortedSet
entrada, tenemos que proporcionar un retroceso para el caso teórico de un SortedSet
no ser NavigableSet
.
Entonces, la operación puede ser implementado como
public static MyEvent getMostRecentLastEventOfSequence(
SortedSet<MyEvent> events, List<String> sequence) {
String first = sequence.get(0), second = sequence.get(1);
UnaryOperator<MyEvent> previous;
if (events instanceof NavigableSet) {
NavigableSet<MyEvent> navigableSet = (NavigableSet<MyEvent>) events;
previous = navigableSet::lower;
}
else previous = event -> events.headSet(event).last();
return events.stream()
.filter(event -> event.getAbbreviation().equals(second))
.filter(event -> {
MyEvent p = previous.apply(event);
return p != null && p.getAbbreviation().equals(first);
})
.max(Comparator.naturalOrder()).orElse(null);
}
pero podemos hacerlo mejor que eso. Puesto que ahora estamos en busca de un máximo en una ordenada de entrada, sabemos que el primer partido es suficiente cuando la iteración hacia atrás. Una vez más, es mucho más suave cuando la entrada es en realidad una NavigableSet
:
public static MyEvent getMostRecentLastEventOfSequence(
SortedSet<MyEvent> events, List<String> sequence) {
String first = sequence.get(0), second = sequence.get(1);
UnaryOperator<MyEvent> previous;
Stream<MyEvent> stream;
if (events instanceof NavigableSet) {
NavigableSet<MyEvent> navigableSet = (NavigableSet<MyEvent>) events;
previous = navigableSet::lower;
stream = navigableSet.descendingSet().stream();
}
else {
previous = event -> events.headSet(event).last();
stream = Stream.iterate(events.last(), previous).limit(events.size());
}
return stream
.filter(event -> event.getAbbreviation().equals(second))
.filter(event -> {
MyEvent p = previous.apply(event);
return p != null && p.getAbbreviation().equals(first);
})
.findFirst().orElse(null);
}
Por lo que este método será buscar hacia atrás y parada en el primer partido, que ya será el elemento máximo, sin la necesidad de recorrer todos los elementos.