Bitrock logo

Sequenced Collections in Java 21

Sequenced Collections

Il 19 settembre 2023 è stata rilasciata una nuova versione di Java 21. Si tratta di una versione LTS. Si tratta di una versione LTS e queste sono le caratteristiche che la contraddistinguono:

In questo articolo daremo uno sguardo più approfondito alla JEP 431, le Sequenced Collections.

Il filosofo danese Sơren Kierkegaard sostiene che la comprensione degli eventi può avvenire solo dopo averli vissuti: “La vita può essere compresa solo all’indietro; ma deve essere vissuta in avanti”. Gli sviluppatori della versione 21 di Java si sono probabilmente ispirati a queste parole quando hanno introdotto nuove interfacce per rappresentare collezioni con un ordine di occorrenza definito. Ognuna di queste collezioni ha un primo elemento ben definito, un secondo elemento e così via, fino all’ultimo elemento. Inoltre, vengono fornite API uniformi per accedere al primo e all’ultimo elemento ed elaborare i loro elementi in ordine inverso.

Accesso ordinato nelle collezioni Java

Un problema di lunga data del framework delle collezioni di Java è la mancanza di un singolo tipo di collezione che garantisca un ordine specifico per i suoi elementi. Questo ha causato problemi e frustrazioni agli sviluppatori. Inoltre, il framework è storicamente privo di metodi coerenti per accedere ai primi e agli ultimi elementi delle collezioni o per iterare facilmente le collezioni in ordine inverso.

Consideriamo collections come List e Deque. Entrambe mantengono un ordine particolare per i loro elementi, ma il loro supertipo comune, Collection, non garantisce l’ordine. Allo stesso modo, Set non ha un ordine, ma alcuni dei suoi sottotipi, come SortedSet e LinkedHashSet, lo hanno. Questa incoerenza significa che il supporto per l’accesso ordinato è disperso nella gerarchia delle collezioni e i metodi per lavorare con l’ordine sono incoerenti o del tutto assenti.

Le nuove implementazioni delle collezioni sequenziate di Java

Ecco una descrizione dettagliata di come gli sviluppatori hanno integrato le collezioni sequenziate nell’esistente Java Collections Framework.

Adattamenti della superinterfaccia

  • List e Deque: Queste due interfacce, che già mantengono l’ordine, ora estendono direttamente SequencedCollection. Questo assicura che ereditino le funzionalità relative all’accesso al primo e all’ultimo elemento, oltre a ottenere una vista inversa.
  • LinkedHashSet: Questa implementazione dell’insieme, che preserva l’ordine di inserimento, ora implementa anche SequencedSet. Ciò consente di utilizzare metodi specifici per gli insiemi ordinati, come l’aggiunta di elementi in posizioni specifiche, pur mantenendo l’ordine complessivo.
  • SortedSet: Questa interfaccia, che mantiene intrinsecamente l’ordine in base a un criterio di ordinamento, ora estende SequencedSet. Questo rafforza la natura ordinata di SortedSet e fornisce l’accesso a funzioni come first() e last(). È importante notare che le implementazioni di SortedSet come TreeSet possono ancora utilizzare la loro logica di ordinamento specifica insieme ai metodi di SequencedSet.
  • LinkedHashMap e SortedMap: Simili a LinkedHashSet e SortedSet, queste interfacce, che mantengono l’ordine in base all’inserimento o all’ordinamento delle chiavi rispettivamente, ora implementano anche SequencedMap. Ciò consente loro di beneficiare di metodi come firstEntry() e lastEntry() , progettati specificamente per le mappe ordinate.

Sovrascritture covarianti per l’inversione

Per garantire la sicurezza dei tipi e la chiarezza, gli sviluppatori hanno implementato delle sovrascritture covarianti per il metodo reversed () Ciò significa che quando viene chiamato su uno specifico tipo di collezione sequenziata (ad esempio, List), anche la vista invertita restituita sarà di quel tipo specifico (ad esempio, List). Questo evita potenziali problemi con tipi di ritorno inaspettati. Ad esempio, List::reversed è sovrascritto per restituire un List invece di una SequencedCollection.

Nuovi metodi nella classe Collections

La classe di utilità Collections è stata estesa con nuovi metodi per creare wrapper non modificabili per i tre nuovi tipi di sequenze:

  • Collections.unmodifiableSequencedCollection(sequencedCollection): Questo metodo prende una SequencedCollection e ne restituisce una versione non modificabile. Ciò consente agli sviluppatori di creare versioni di sola lettura di collezioni sequenziate, per evitare modifiche accidentali.
  • Collections.unmodifiableSequencedSet(sequencedSet): Simile al metodo precedente, prende un SequencedSet e restituisce la sua controparte non modificabile.
  • Collections.unmodifiableSequencedMap(sequencedMap): Questo metodo crea una versione non modificabile di una SequencedMap.

Questi nuovi metodi promuovono l’immutabilità dei dati, che può essere utile per la sicurezza e la protezione dei thread in ambienti concorrenti. Integrando le Sequenced Collections in questo modo, gli sviluppatori hanno migliorato in modo significativo la coerenza e l’usabilità delle collezioni ordinate nel Java Collections Framework.

Esplorare la funzionalità delle Sequenced Collections

Vediamo come queste interfacce vengono utilizzate in pratica:

Elaborazioni di un file di log di grandi dimensioni in ordine inverso

Immaginiamo uno scenario in cui sia necessario analizzare un file di log di grandi dimensioni, iniziando dalle voci più recenti. L’approccio più semplice sarebbe quello di leggere l’intero file riga per riga, ma questo può essere inefficiente per file di grandi dimensioni. Le collezioni e i flussi equenziati vengono in soccorso.

Coda di attività prioritaria con aggiornamenti dinamici

Si consideri un sistema di pianificazione delle attività in cui le attività arrivano con priorità diverse. È necessario mantenere una coda che dia priorità alle attività in base alla loro urgenza, consentendo al contempo aggiornamenti dinamici (inserimento e rimozione) delle attività.

Rischi e considerazioni sull’introduzione di Sequenced Collections

Sebbene le collezioni sequenziate offrano vantaggi significativi, la loro introduzione non è stata priva di sfide. Ecco una panoramica dei principali rischi e presupposti presi in considerazione dagli sviluppatori.

Method Name Collisions

L’aggiunta di nuovi metodi in alto nella gerarchia ereditaria può causare conflitti con i nomi dei metodi esistenti nelle sottoclassi. Metodi come reversed() e getFirst() possono potenzialmente entrare in conflitto con le implementazioni esistenti, causando un comportamento inaspettato.

Sovrascritture covarianti e incompatibilità binaria

Un problema particolare era rappresentato dalle sovrascritture covarianti per il metodo reversed() sia su List e Deque. Se da un lato questo approccio garantisce la sicurezza dei tipi, dall’altro introduce incompatibilità di tipo sorgente e binario con le collezioni esistenti che implementano entrambe le interfacce. Questo potrebbe potenzialmente interrompere il codice che si basa su queste collezioni.

Impatto sulle implementazioni esistenti

Sono stati colpiti due esempi nel JDK: LinkedList e una classe interna sun.awt.util.IdentityLinkedList.

  • LinkedList è stato risolto introducendo un nuovo reversed() specifico per LinkedList.
  • La classe interna IdentityLinkedList, ritenuta non necessaria, è stata eliminata.

Bilanciare rischi e benefici

Una proposta precedente considerava le sovrascritture covarianti per i metodi keySet(), values() e entrySet() di SequencedMap. Tuttavia, questo approccio comportava un elevato rischio di rottura delle sottoclassi esistenti, a causa del significativo cambiamento di comportamento.

Di conseguenza, gli sviluppatori hanno optato per un approccio più sicuro:

  • sono stati aggiunti a SequencedMap nuovi metodi: sequencedKeySet(), sequencedValues(), e sequencedEntrySet()

In questo modo si è evitato di modificare i metodi esistenti e di ridurre al minimo le potenziali incompatibilità.

Conclusioni

Gli sviluppatori delle collezioni sequenziate hanno considerato attentamente i rischi potenziali e hanno preso decisioni informate per ridurre al minimo le interruzioni e mantenere la compatibilità con le basi di codice esistenti. Sebbene siano stati necessari alcuni aggiustamenti (come nel caso di LinkedList), l’approccio complessivo ha garantito una transizione più agevole verso un modo più coerente di gestire le collezioni ordinate in Java.


Autore: Luigi Cerrato, Software Engineer @ Bitrock

Vuoi saperne di più sui nostri servizi? Compila il modulo e fissa un incontro con il nostro team!

Skip to content