Perché Java 8 non include raccolte immutabili?


130

Il team Java ha svolto moltissimo lavoro rimuovendo gli ostacoli alla programmazione funzionale in Java 8. In particolare, le modifiche alle raccolte java.util svolgono un ottimo lavoro nel concatenare le trasformazioni in operazioni in streaming molto veloce. Considerando quanto sono stati bravi a fare un lavoro aggiungendo funzioni e metodi funzionali di prima classe sulle collezioni, perché non sono riusciti a fornire collezioni immutabili o persino interfacce di raccolta immutabili?

Senza modificare alcun codice esistente, il team Java potrebbe in qualsiasi momento aggiungere interfacce immutabili uguali a quelle mutabili, meno i metodi "set" e fare in modo che le interfacce esistenti si estendano da esse, in questo modo:

                  ImmutableIterable
     ____________/       |
    /                    |
Iterable        ImmutableCollection
   |    _______/    /          \   \___________
   |   /           /            \              \
 Collection  ImmutableList  ImmutableSet  ImmutableMap  ...
    \  \  \_________|______________|__________   |
     \  \___________|____________  |          \  |
      \___________  |            \ |           \ |
                  List            Set           Map ...

Certo, operazioni come List.add () e Map.put () attualmente restituiscono un valore booleano o precedente per la chiave data per indicare se l'operazione ha avuto esito positivo o negativo. Le raccolte immutabili dovrebbero trattare tali metodi come fabbriche e restituire una nuova raccolta contenente l'elemento aggiunto, che è incompatibile con la firma corrente. Ma questo potrebbe essere aggirato usando un nome di metodo diverso come ImmutableList.append () o .addAt () e ImmutableMap.putEntry (). La verbosità risultante sarebbe più che compensata dai vantaggi di lavorare con raccolte immutabili, e il sistema dei tipi impedirebbe errori nel chiamare il metodo sbagliato. Nel tempo, i vecchi metodi potrebbero essere deprecati.

Vittorie di collezioni immutabili:

  • Semplicità: il ragionamento sul codice è più semplice quando i dati sottostanti non cambiano.
  • Documentazione: se un metodo accetta un'interfaccia di raccolta immutabile, sai che non modificherà quella raccolta. Se un metodo restituisce una raccolta immutabile, sai che non puoi modificarlo.
  • Concorrenza: le raccolte immutabili possono essere condivise in modo sicuro tra thread.

Come qualcuno che ha assaggiato le lingue che assumono immutabilità, è molto difficile tornare nel selvaggio West di mutazione dilagante. Le raccolte di Clojure (astrazione di sequenza) hanno già tutto ciò che le raccolte di Java 8 forniscono, oltre all'immutabilità (anche se forse usano memoria e tempo extra a causa di liste collegate sincronizzate invece di flussi). Scala ha collezioni sia mutabili sia immutabili con una serie completa di operazioni, e sebbene quelle operazioni siano entusiaste, chiamare .iterator offre una visione pigra (e ci sono altri modi per valutarle pigramente). Non vedo come Java possa continuare a competere senza raccolte immutabili.

Qualcuno può indicarmi la storia o la discussione su questo? Sicuramente è pubblico da qualche parte.


9
In relazione a ciò - Ayende ha recentemente pubblicato un blog sulle raccolte e sulle collezioni immutabili in C #, con benchmark. ayende.com/blog/tags/performance - tl; dr - l'immutabilità è lenta .
Oded,

20
con la tua gerarchia posso darti un ImmutableList e poi cambiarlo su di te quando non ti aspetti che possa rompere molte cose, dato che hai solo constraccolte
maniaco del cricco

18
@Oded Immutability è lento, ma lo è anche il blocco. Quindi sta mantenendo una storia. La semplicità / correttezza vale la velocità in molte situazioni. Con piccole raccolte, la velocità non è un problema. L'analisi di Ayende si basa sul presupposto che non sono necessari cronologia, blocco o semplicità e che si sta lavorando con un set di dati di grandi dimensioni. A volte è vero, ma non è una cosa sempre migliore. Ci sono compromessi.
GlenPeterson,

5
@GlenPeterson ecco a cosa servono le copie difensive Collections.unmodifiable*(). ma non trattarli come immutabili quando non lo sono
maniaco del cricchetto,

13
Eh? Se la tua funzione prende ImmutableListin quel diagramma, le persone possono passare in un mutevole List? No, questa è una molto brutta violazione della SPA.
Telastyn,

Risposte:


113

Perché le raccolte immutabili richiedono assolutamente la condivisione per essere utilizzabili. Altrimenti, ogni singola operazione rilascia un altro intero elenco nell'heap da qualche parte. Le lingue che sono completamente immutabili, come Haskell, generano quantità sorprendenti di immondizia senza ottimizzazioni e condivisione aggressive. Avere una raccolta utilizzabile solo con <50 elementi non vale la pena inserire nella libreria standard.

Inoltre, le collezioni immutabili spesso hanno implementazioni sostanzialmente diverse rispetto alle loro controparti mutabili. Considera ad esempio che ArrayListun immutabile efficiente ArrayListnon sarebbe affatto un array! Dovrebbe essere implementato con un albero bilanciato con un grande fattore di ramificazione, Clojure utilizza 32 IIRC. Rendere le raccolte mutabili "immutabili" semplicemente aggiungendo un aggiornamento funzionale è un errore prestazionale tanto quanto una perdita di memoria.

Inoltre, la condivisione non è praticabile in Java. Java fornisce troppi agganci illimitati alla mutabilità e all'uguaglianza di riferimento per rendere la condivisione "solo un'ottimizzazione". Probabilmente ti farebbe un po 'male se tu potessi modificare un elemento in un elenco, e realizzare che hai appena modificato un elemento nelle altre 20 versioni di quell'elenco che avevi.

Questo esclude anche enormi classi di ottimizzazioni molto vitali per l'immutabilità efficiente, la condivisione, la fusione del flusso, lo chiami, la mutabilità la rompe. (Sarebbe un buon slogan per gli evangelisti del PQ)


21
Il mio esempio parlava di interfacce immutabili . Java potrebbe fornire una suite completa di implementazioni sia mutabili che immutabili di quelle interfacce che renderebbero i necessari compromessi. Spetta al programmatore scegliere mutabile o immutabile come appropriato. I programmatori devono sapere quando utilizzare un Elenco vs. Imposta ora. Generalmente non è necessaria la versione modificabile fino a quando non si riscontra un problema di prestazioni, quindi potrebbe essere necessario solo come builder. In ogni caso, avere l'interfaccia immutabile sarebbe una vittoria a sé stante.
GlenPeterson,

4
Ho letto di nuovo la tua risposta e penso che tu stia dicendo che Java ha un presupposto fondamentale della mutabilità (ad es. Java bean) e che le raccolte sono solo la punta dell'iceberg e ritagliarsi quella punta non risolverà il problema di fondo. Un punto valido Potrei accettare questa risposta e accelerare la mia adozione alla Scala! :-)
GlenPeterson il

8
Non sono sicuro che le raccolte immutabili richiedano la capacità di condividere parti comuni per essere utili. Il tipo immutabile più comune in Java, una raccolta immutabile di personaggi, utilizzato per consentire la condivisione ma non lo fa più. La cosa chiave che lo rende utile è la capacità di copiare rapidamente i dati da a Stringin a StringBuffer, manipolarli e quindi copiare i dati in un nuovo immutabile String. L'uso di un tale modello con set ed elenchi potrebbe essere buono come l'uso di tipi immutabili progettati per facilitare la produzione di istanze leggermente modificate, ma potrebbe comunque essere migliore ...
Supercat,

3
È possibile creare una raccolta immutabile in Java usando la condivisione. Gli articoli archiviati nella raccolta sono riferimenti e i loro riferimenti potrebbero essere mutati - e allora? Tale comportamento interrompe già raccolte esistenti come HashMap e TreeSet, ma quelle sono implementate in Java. E se più raccolte contengono riferimenti allo stesso oggetto, è del tutto previsto che la modifica dell'oggetto provocherà una modifica visibile quando viene visualizzata da tutte le raccolte.
Solomonoff's Secret

4
jozefg, è del tutto possibile implementare raccolte immutabili efficienti su JVM con condivisione strutturale. Scala e Clojure li hanno come parte della loro libreria standard, entrambe le implementazioni sono basate sull'HAMT di Phil Bagwell (Hash Array Mapped Trie). La tua dichiarazione in merito a Clojure che implementa strutture dati immutabili con alberi BILANCIATI è completamente sbagliata.
sesm,

78

Una collezione mutabile non è un sottotipo di una collezione immutabile. Invece, le collezioni mutabili e immutabili sono discendenti fratelli di raccolte leggibili. Sfortunatamente, i concetti di "leggibile", "sola lettura" e "immutabile" sembrano sfocati insieme, anche se significano tre cose diverse.

  • Una classe di base di raccolta leggibile o un tipo di interfaccia promette che si possono leggere elementi e non fornisce alcun mezzo diretto per modificare la raccolta, ma non garantisce che il codice che riceve il riferimento non possa lanciarlo o manipolarlo in modo da consentire la modifica.

  • Un'interfaccia di raccolta di sola lettura non include nuovi membri, ma dovrebbe essere implementata solo da una classe che promette che non c'è modo di manipolare un riferimento ad esso in modo tale da mutare la raccolta o ricevere un riferimento a qualcosa quello potrebbe farlo. Tuttavia, non promette che la collezione non sarà modificata da qualcos'altro che abbia un riferimento agli interni. Si noti che un'interfaccia di raccolta di sola lettura potrebbe non essere in grado di impedire l'implementazione da parte di classi mutabili, ma può specificare che qualsiasi implementazione o classe derivata da un'implementazione, che consente la mutazione, deve essere considerata un'implementazione "illegittima" o derivata di un'implementazione .

  • Una raccolta immutabile è quella che conterrà sempre gli stessi dati fintanto che esiste un riferimento ad essi. Qualsiasi implementazione di un'interfaccia immutabile che non restituisce sempre gli stessi dati in risposta a una particolare richiesta viene interrotta.

Talvolta è utile avere tipi fortemente associati mutabili e immutabili raccolta che sia implementare o derivare dal medesimo tipo "leggibili", e di avere il tipo leggibile includere AsImmutable, AsMutablee AsNewMutablemetodi. Una tale progettazione può consentire al codice che desidera mantenere persistenti i dati in una raccolta AsImmutable; quel metodo farà una copia difensiva se la collezione è mutabile, ma salta la copia se è già immutabile.


1
Bella risposta. Le collezioni immutabili possono darti una garanzia abbastanza forte legata alla sicurezza dei thread e al modo in cui puoi ragionare su di loro col passare del tempo. Una raccolta leggibile / di sola lettura no. In effetti, per onorare il principio della sottostazione di Liskov, la sola lettura e l'immutabile dovrebbero probabilmente essere di tipo astratto con metodo finale e membri privati ​​per garantire che nessuna classe derivata possa distruggere la garanzia fornita dal tipo. Oppure dovrebbero essere del tipo completamente concreto che avvolge una raccolta (sola lettura) o ne prenda sempre una copia difensiva (immutabile). Ecco come funziona Guava ImmutableList.
Laurent Bourgault-Roy il

1
@ LaurentBourgault-Roy: ci sono vantaggi sia per i tipi sigillati che per quelli immutabili. Se uno non vuole consentire a una classe derivata illegittima di infrangere i propri invarianti, i tipi sigillati possono offrire protezione contro ciò mentre le classi ereditabili non offrono nessuno. D'altra parte, potrebbe essere possibile per il codice che conosce qualcosa sui dati in suo possesso archiviarlo in modo molto più compatto rispetto a un tipo che non ne sa nulla. Si consideri, ad esempio, un tipo ReadableIndexedIntSequence che incapsula una sequenza di int, con metodi getLength()e getItemAt(int).
supercat il

1
@ LaurentBourgault-Roy: Dato un ReadableIndexedIntSequence, si potrebbe produrre un'istanza di un tipo immutabile supportato da array copiando tutti gli elementi in un array, ma supponiamo che un'implementazione particolare abbia semplicemente restituito 16777216 per lunghezza e ((long)index*index)>>24per ogni elemento. Sarebbe una sequenza legittima immutabile di numeri interi, ma copiarla su un array sarebbe una grande perdita di tempo e memoria.
supercat il

1
Sono completamente d'accordo. La mia soluzione ti dà la correttezza (fino a un certo punto), ma per ottenere prestazioni con un set di dati di grandi dimensioni, devi avere struttura e design persistenti per l'immutabilità fin dall'inizio. Per le raccolte di piccole dimensioni, tuttavia, è possibile cavarsela con una copia immutabile di volta in volta. Ricordo che Scala fece alcune analisi di vari programmi e scoprì che qualcosa come il 90% delle liste istanziate era lungo 10 o meno voci.
Laurent Bourgault-Roy il

1
@ LaurentBourgault-Roy: la domanda fondamentale è se uno si fida delle persone per non produrre implementazioni rotte o classi derivate. In tal caso, e se le interfacce / classi di base forniscono metodi asMutable / asImmutable, potrebbe essere possibile migliorare le prestazioni di molti ordini di grandezza [ad esempio confrontare il costo della chiamata asImmutablesu un'istanza della sequenza sopra definita rispetto al costo di costruzione una copia immutabile supportata da array]. Suppongo che avere interfacce definite per tali scopi sia probabilmente meglio che provare ad usare approcci ad hoc; IMHO, la ragione più grande ...
supercat il

15

Java Collections Framework offre la possibilità di creare una versione di sola lettura di una raccolta tramite sei metodi statici nella classe java.util.Collections :

Come qualcuno ha sottolineato nei commenti alla domanda originale, le raccolte restituite potrebbero non essere considerate immutabili perché anche se le raccolte non possono essere modificate (nessun membro può essere aggiunto o rimosso da tale raccolta), gli oggetti reali a cui fa riferimento la raccolta può essere modificato se il loro tipo di oggetto lo consente.

Tuttavia, questo problema rimarrebbe indipendentemente dal fatto che il codice restituisca un singolo oggetto o una raccolta di oggetti non modificabile. Se il tipo consente ai suoi oggetti di essere mutati, allora quella decisione è stata presa nella progettazione del tipo e non vedo come una modifica al JCF possa alterarlo. Se l'immutabilità è importante, i membri di una raccolta dovrebbero essere di tipo immutabile.


4
Il design delle collezioni non modificabili sarebbe stato notevolmente migliorato se gli involucri includessero un'indicazione se l'oggetto da avvolgere fosse già immutabile, e c'erano immutableListmetodi di fabbrica ecc. Che avrebbero restituito un involucro di sola lettura attorno a una copia di un pass-in list a meno che l' elenco passato non fosse già immutabile . Sarebbe facile creare tipi definiti dall'utente come quello, ma per un problema: non ci sarebbe modo per il joesCollections.immutableListmetodo di riconoscere che non avrebbe bisogno di copiare l'oggetto restituito da fredsCollections.immutableList.
supercat

8

Questa è un'ottima domanda Mi piace intrattenere l'idea che di tutto il codice scritto in java e in esecuzione su milioni di computer in tutto il mondo, ogni giorno, 24 ore su 24, circa la metà dei cicli di clock totali debba essere sprecata facendo altro che fare copie di sicurezza di raccolte che sono essere restituito da funzioni. (E immondizia raccogliendo queste raccolte millisecondi dopo la loro creazione.)

Una percentuale di programmatori Java è consapevole dell'esistenza della unmodifiableCollection()famiglia di metodi della Collectionsclasse, ma anche tra questi, molti semplicemente non si preoccupano.

E non posso biasimarli: un'interfaccia che finge di essere di lettura-scrittura, ma UnsupportedOperationExceptionse si commette l'errore di invocare uno dei suoi metodi di "scrittura" è una cosa piuttosto cattiva!

Ora, un'interfaccia simile Collection, che sarebbe mancato il add(), remove()e clear()metodi non sarebbe un'interfaccia "ImmutableCollection"; sarebbe un'interfaccia "UnmodifiableCollection". È un dato di fatto, non ci potrebbe mai essere un'interfaccia "ImmutableCollection", perché l'immutabilità è una natura di un'implementazione, non una caratteristica di un'interfaccia. Lo so, non è molto chiaro; lasciatemi spiegare.

Supponiamo che qualcuno ti dia una tale interfaccia di raccolta di sola lettura; è sicuro passarlo a un altro thread? Se sapessi per certo che rappresenta una collezione davvero immutabile, la risposta sarebbe "sì"; sfortunatamente, dal momento che è un'interfaccia, non sai come sia implementata, quindi la risposta deve essere un no : per quanto ne sai, potrebbe essere una visione non modificabile (per te) di una raccolta che è in realtà mutabile, (come quello che ottieni Collections.unmodifiableCollection()), quindi il tentativo di leggere da esso mentre un altro thread sta modificando comporterebbe la lettura di dati corrotti.

Quindi, ciò che hai sostanzialmente descritto è un insieme di interfacce di raccolta non "immutabili", ma "non modificabili". È importante capire che "Non modificabile" significa semplicemente che a chiunque abbia un riferimento a tale interfaccia è impedito di modificare la raccolta sottostante, e sono impediti semplicemente perché l'interfaccia manca di metodi di modifica, non perché la raccolta sottostante è necessariamente immutabile. La collezione sottostante potrebbe benissimo essere mutevole; non ne hai conoscenza e non ne hai il controllo.

Per avere collezioni immutabili, dovrebbero essere classi , non interfacce!

Queste classi di raccolta immutabili dovrebbero essere definitive, in modo che quando ti viene dato un riferimento a tale raccolta, sai per certo che si comporterà come una raccolta immutabile, indipendentemente da ciò che tu o chiunque altro abbia un riferimento ad essa, potrebbe fare con esso.

Quindi, per avere un set completo di raccolte in java (o in qualsiasi altro linguaggio imperativo dichiarativo) avremmo bisogno di quanto segue:

  1. Un insieme di interfacce di raccolta non modificabili .

  2. Un insieme di interfacce di raccolta mutabili , che ampliano quelle non modificabili.

  3. Un insieme di classi di raccolta mutabili che implementano le interfacce mutabili e, per estensione, anche le interfacce non modificabili.

  4. Un insieme di classi di raccolta immutabili , che implementano le interfacce non modificabili, ma per lo più passate in giro come classi, in modo da garantire l'immutabilità.

Ho implementato tutto quanto sopra per divertimento e li sto usando nei progetti e funzionano come un fascino.

Il motivo per cui non fanno parte del runtime Java è probabilmente perché si pensava che questo sarebbe troppo / troppo complesso / troppo difficile da capire.

Personalmente, penso che ciò che ho descritto sopra non sia nemmeno abbastanza; un'altra cosa che sembra essere necessaria è un insieme di interfacce e classi mutabili per l' immutabilità strutturale . (Che può semplicemente essere chiamato "Rigido" perché il prefisso "Strutturalmente immutabile" è troppo dannatamente lungo.)


Punti buoni. Due dettagli: 1. Le raccolte immutabili richiedono determinate firme di metodo, in particolare (utilizzando un Elenco come esempio): List<T> add(T t)- tutti i metodi "mutatori" devono restituire una nuova raccolta che riflette la modifica. 2. Nel bene o nel male, le interfacce spesso rappresentano un contratto oltre a una firma. Serializable è una di queste interfacce. Allo stesso modo, Comparable richiede l'implementazione corretta del compareTo()metodo per funzionare correttamente e idealmente essere compatibile con equals()e hashCode().
GlenPeterson,

Oh, non avevo nemmeno in mente l'immutabilità mutata per copia. Quello che ho scritto sopra si riferisce a semplici raccolte immutabili che in realtà non hanno metodi simili add(). Ma suppongo che se si dovessero aggiungere metodi mutatori alle classi immutabili, allora dovrebbero restituire anche classi immutabili. Quindi, se c'è un problema in agguato lì, non lo vedo.
Mike Nakis,

La tua implementazione è disponibile al pubblico? Avrei dovuto chiedere questo mesi fa. Comunque, il mio è: github.com/GlenKPeterson/UncleJim
GlenPeterson

4
Suppose someone hands you such a read-only collection interface; is it safe to pass it to another thread?Supponiamo che qualcuno ti passi un'istanza di un'interfaccia di raccolta mutabile. È sicuro invocare qualche metodo su di esso? Non sai che l'implementazione non esegue un ciclo continuo, genera un'eccezione o ignora completamente il contratto dell'interfaccia. Perché avere un doppio standard specifico per le collezioni immutabili?
Doval,

1
IMHO il tuo ragionamento contro le interfacce mutabili è sbagliato. È possibile scrivere un'implementazione mutevole di interfacce immutabili e quindi si interrompe. Sicuro. Ma è colpa tua se stai violando il contratto. Basta smettere di farlo. Non è diverso dalla rottura di una SortedSetsottoclasse del set con un'implementazione non conforme. O passando un incoerente Comparable. Quasi tutto può essere rotto se vuoi. Immagino, questo è ciò che @Doval intendeva per "doppi standard".
maaartinus,

2

Le raccolte immutabili possono essere profondamente ricorsive, se confrontate tra loro, e non irragionevolmente inefficienti se l'uguaglianza degli oggetti è garantita da secureHash. Questa si chiama foresta di merkle. Può essere per raccolta o all'interno di parti di essi come un albero AVL (binario autobilanciante) per una mappa ordinata.

A meno che tutti gli oggetti Java in queste raccolte non abbiano un ID univoco o una stringa di bit da hash, la raccolta non ha nulla da hash per nominare in modo univoco.

Esempio: sul mio laptop 4x1.6ghz, posso eseguire 200K sha256s al secondo della dimensione più piccola che si adatta in 1 ciclo di hash (fino a 55 byte), rispetto alle operazioni HashMap 500K o 3M in una tabella hash di long. 200K / log (collectionSize) nuove raccolte al secondo sono abbastanza veloci per alcune cose in cui l'integrità dei dati e la scalabilità globale anonima sono importanti.


-3

Prestazione. Le collezioni per loro natura possono essere molto grandi. Copiare 1000 elementi in una nuova struttura con 1001 elementi invece di inserire un singolo elemento è semplicemente orribile.

Concorrenza. Se sono in esecuzione più thread, è possibile che si desideri ottenere la versione corrente della raccolta e non la versione passata 12 ore fa all'avvio del thread.

Conservazione. Con oggetti immutabili in un ambiente multi-thread puoi finire con dozzine di copie dello "stesso" oggetto in diversi punti del suo ciclo di vita. Non importa per un oggetto Calendario o Data ma quando è una raccolta di 10.000 widget questo ti ucciderà.


12
Le raccolte immutabili richiedono la copia solo se non è possibile condividerle a causa della mutabilità pervasiva come ha fatto Java. La concorrenza è generalmente più semplice con raccolte immutabili perché non richiedono il blocco; e per visibilità puoi sempre avere un riferimento mutevole a una collezione immutabile (comune in OCaml). Con la condivisione, gli aggiornamenti possono essere essenzialmente gratuiti. È possibile effettuare logaritmicamente più allocazioni rispetto a una struttura mutabile, ma durante l'aggiornamento, molti oggetti secondari scaduti possono essere liberati immediatamente o riutilizzati, quindi non è necessario un overhead di memoria maggiore.
Jon Purdy,

4
Problemi di coppia. Le collezioni di Clojure e Scala sono entrambe immutabili, ma supportano copie leggere. Aggiungere l'elemento 1001 significa copiare meno di 33 elementi, oltre a creare alcuni nuovi puntatori. Se condividi una raccolta mutevole tra thread, hai tutti i tipi di problemi di sincronizzazione quando la cambi. Operazioni come "remove ()" sono da incubo. Inoltre, le raccolte immutabili possono essere costruite in modo mutevole, quindi copiate una volta in una versione immutabile sicura da condividere tra i thread.
GlenPeterson,

4
L'uso della concorrenza come argomento contro l'immutabilità è insolito. Anche i duplicati.
Tom Hawtin - tackline il

4
Un po 'seccato per i voti negativi qui. L'OP ha chiesto perché non implementassero collezioni immutabili e, ho fornito una risposta ponderata alla domanda. Presumibilmente l'unica risposta accettabile tra i più attenti alla moda è "perché hanno fatto un errore". In realtà ho una certa esperienza con questo dover refactoring di grossi blocchi di codice usando la classe BigDecimal altrimenti eccellente puramente a causa della scarsa prestazione dovuta all'immutabilità 512 volte quella di usare un doppio più un po 'di pasticciare per correggere i decimali.
James Anderson,

3
@JamesAnderson: I miei problemi con la tua risposta: "Performance" - potresti dire che le collezioni immutabili della vita reale implementano sempre una qualche forma di condivisione e riutilizzo per evitare esattamente il problema che descrivi. "Concorrenza" - l'argomento si riduce a "Se si desidera la mutabilità, allora un oggetto immutabile non funziona." Voglio dire che se c'è una nozione di "ultima versione della stessa cosa", allora qualcosa deve mutare, o la cosa stessa, o qualcosa che possiede la cosa. E in "Archiviazione", sembra che a volte non si desideri la mutabilità.
jhominal
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.