Qual è la complessità temporale del String#substring()
metodo in Java?
Qual è la complessità temporale del String#substring()
metodo in Java?
Risposte:
Nuova risposta
A partire dall'aggiornamento 6 durante la vita di Java 7, il comportamento è substring
cambiato per creare una copia, quindi ogni si String
riferisce a un oggetto char[]
che non è condiviso con nessun altro oggetto, per quanto ne so. Quindi, a quel punto, è substring()
diventata un'operazione O (n) dove n è il numero nella sottostringa.
Vecchia risposta: pre-Java 7
Non documentato, ma in pratica O (1) se si presume che non sia richiesta la garbage collection, ecc.
Costruisce semplicemente un nuovo String
oggetto che fa riferimento allo stesso sottostante char[]
ma con valori di offset e conteggio diversi. Quindi il costo è il tempo impiegato per eseguire la convalida e costruire un singolo oggetto nuovo (ragionevolmente piccolo). Questo è O (1) per quanto è sensato parlare della complessità delle operazioni che possono variare nel tempo in base alla garbage collection, cache della CPU ecc. In particolare, non dipende direttamente dalla lunghezza della stringa originale o della sottostringa .
Era O (1) nelle versioni precedenti di Java - come ha affermato Jon, ha appena creato una nuova stringa con lo stesso carattere sottostante [] e un offset e una lunghezza diversi.
Tuttavia, questo è effettivamente cambiato a partire dall'aggiornamento 6 di Java 7.
La condivisione char [] è stata eliminata e i campi offset e lunghezza sono stati rimossi. substring () ora copia semplicemente tutti i caratteri in una nuova stringa.
Ergo, la sottostringa è O (n) nell'aggiornamento 6 di Java 7
char[]
...
Adesso è una complessità lineare. Questo è dopo aver risolto un problema di perdita di memoria per la sottostringa.
Quindi da Java 1.7.0_06 ricorda che String.substring ora ha una complessità lineare invece di una costante.
Aggiunta di prove alla risposta di Jon. Avevo lo stesso dubbio e volevo verificare se la lunghezza della stringa ha effetti sulla funzione della sottostringa. Scritto il codice seguente per verificare da quale sottostringa di parametro dipende effettivamente.
import org.apache.commons.lang.RandomStringUtils;
public class Dummy {
private static final String pool[] = new String[3];
private static int substringLength;
public static void main(String args[]) {
pool[0] = RandomStringUtils.random(2000);
pool[1] = RandomStringUtils.random(10000);
pool[2] = RandomStringUtils.random(100000);
test(10);
test(100);
test(1000);
}
public static void test(int val) {
substringLength = val;
StatsCopy statsCopy[] = new StatsCopy[3];
for (int j = 0; j < 3; j++) {
statsCopy[j] = new StatsCopy();
}
long latency[] = new long[3];
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 3; j++) {
latency[j] = latency(pool[j]);
statsCopy[j].send(latency[j]);
}
}
for (int i = 0; i < 3; i++) {
System.out.println(
" Avg: "
+ (int) statsCopy[i].getAvg()
+ "\t String length: "
+ pool[i].length()
+ "\tSubstring Length: "
+ substringLength);
}
System.out.println();
}
private static long latency(String a) {
long startTime = System.nanoTime();
a.substring(0, substringLength);
long endtime = System.nanoTime();
return endtime - startTime;
}
private static class StatsCopy {
private long count = 0;
private long min = Integer.MAX_VALUE;
private long max = 0;
private double avg = 0;
public void send(long latency) {
computeStats(latency);
count++;
}
private void computeStats(long latency) {
if (min > latency) min = latency;
if (max < latency) max = latency;
avg = ((float) count / (count + 1)) * avg + (float) latency / (count + 1);
}
public double getAvg() {
return avg;
}
public long getMin() {
return min;
}
public long getMax() {
return max;
}
public long getCount() {
return count;
}
}
}
L'output durante l'esecuzione in Java 8 è:
Avg: 128 String length: 2000 Substring Length: 10
Avg: 127 String length: 10000 Substring Length: 10
Avg: 124 String length: 100000 Substring Length: 10
Avg: 172 String length: 2000 Substring Length: 100
Avg: 175 String length: 10000 Substring Length: 100
Avg: 177 String length: 100000 Substring Length: 100
Avg: 1199 String length: 2000 Substring Length: 1000
Avg: 1186 String length: 10000 Substring Length: 1000
Avg: 1339 String length: 100000 Substring Length: 1000
La funzione di dimostrazione della sottostringa dipende dalla lunghezza della sottostringa richiesta non dalla lunghezza della stringa.
Giudicate voi stessi dal seguire, ma gli svantaggi delle prestazioni di Java risiedono altrove, non qui nella sottostringa di una stringa. Codice:
public static void main(String[] args) throws IOException {
String longStr = "asjf97zcv.1jm2497z20`1829182oqiwure92874nvcxz,nvz.,xo" +
"aihf[oiefjkas';./.,z][p\\°°°°°°°°?!(*#&(@*&#!)^(*&(*&)(*&" +
"fasdznmcxzvvcxz,vc,mvczvcz,mvcz,mcvcxvc,mvcxcvcxvcxvcxvcx";
int[] indices = new int[32 * 1024];
int[] lengths = new int[indices.length];
Random r = new Random();
final int minLength = 6;
for (int i = 0; i < indices.length; ++i)
{
indices[i] = r.nextInt(longStr.length() - minLength);
lengths[i] = minLength + r.nextInt(longStr.length() - indices[i] - minLength);
}
long start = System.nanoTime();
int avoidOptimization = 0;
for (int i = 0; i < indices.length; ++i)
//avoidOptimization += lengths[i]; //tested - this was cheap
avoidOptimization += longStr.substring(indices[i],
indices[i] + lengths[i]).length();
long end = System.nanoTime();
System.out.println("substring " + indices.length + " times");
System.out.println("Sum of lengths of splits = " + avoidOptimization);
System.out.println("Elapsed " + (end - start) / 1.0e6 + " ms");
}
Produzione:
sottostringa 32768 volte Somma delle lunghezze delle suddivisioni = 1494414 Risposta 2.446679 ms
Se è O (1) o no, dipende. Se fai riferimento alla stessa stringa in memoria, immagina una stringa molto lunga, crei una sottostringa e smetti di fare riferimento a una stringa lunga. Non sarebbe bello liberare memoria a lungo?
Prima di Java 1.7.0_06: O (1).
Dopo Java 1.7.0_06: O (n). Ciò è stato modificato a causa della perdita di memoria. Dopo che i campi offset
e count
sono stati rimossi da String, l'implementazione della sottostringa è diventata O (n).
Per maggiori dettagli, fare riferimento a: http://java-performance.info/changes-to-string-java-1-7-0_06/