2015-02-16 11 views
6

Ho due diverse implementazioni di una funzione (ad esempio la dimensione dell'albero) una che è ricorsiva e una che utilizza uno stack esplicito.È sicuro catturare StackOverflowError in Java?

Il ricorsivo è molto veloce (probabilmente perché non ha bisogno di allocare nulla sullo heap) ma può causare uno stack overflow su alcuni input "rari" (nell'esempio dell'albero, sarebbe su qualsiasi albero non bilanciato) . La versione esplicita è più lenta ma è improbabile che causi un overflow dello stack.

Quanto è sicuro utilizzare l'implementazione ricorsiva per impostazione predefinita e ripristinare dall'eccezione StackOverflowError eseguendo quella esplicita?

È considerato una cattiva pratica?

Ecco un piccolo esempio di codice:

interface Node { 
    List<? extends Node> getSons(); 
} 

static int sizeRec (Node root) { 
    int result = 1; 
    for (Node son : root.getSons()) { 
    result += sizeRec(son); 
    } 
    return result; 
} 

static int sizeStack (Node root) { 
    Stack<Node> stack = new Stack<Node>(); 
    stack.add(root); 
    int size = 0; 
    while (! stack.isEmpty()) { 
    Node x = stack.pop(); 
    size ++; 
    for (Node son : x.getSons()) { 
     stack.push(son); 
    } 
    } 
    return size; 
} 

static int size (Node root) { 
    try { 
    return sizeRec(root); 
    } catch (StackOverflowError e) { 
    return sizeStack(root); 
    } 
} 
+0

quanto ne sappia io, non ci sono problemi con la cattura StackOverflowError - è solo una cosa molto raro bisogno di fare, e * la maggior parte del tempo * indica un bug, piuttosto che una struttura di dati troppo complessa per elaborare in modo ricorsivo . – immibis

+0

Come si quantifica più veloce e più lento in questo caso? L'hai punto di riferimento? Se sì, come? –

+3

@immibis: non ne sono così sicuro. Se noti l'eccezione, allora cosa? Probabilmente il tuo stack non è in uno stato sostenibile, quindi come ti riprenderesti da tale errore e sarai certo che il tuo programma sia stabile? –

risposta

3

Beh, questa è una questione di opinione. Ma, non credo che tu debba mai farlo. In primo luogo, la tua logica distorce il significato della gestione delle eccezioni (eccezioni per "eccezioni" non per la logica), altri programmatori avranno problemi nell'interpretazione del tuo codice.

Oltre a ciò non è necessario rilevare "Errori" che indicano problemi di runtime sull'ambiente. Dovresti chiederti se ne vale la pena per dimenticare un po 'le buone pratiche. Forse, potresti provare ad aggiustare la configurazione del runtime per adattarla all'applicazione o aggiungere una logica di verifica extra ... la tua chiamata lì ... ma siccome si considera la sicurezza, non puoi dire che stai bene, dato che non sappiamo come lo stato dello stack è ora, e diverse implementazioni JRE potrebbero differire.

Infine, per la domanda in basso: è una cattiva pratica e non è sicura.

Una citazione da https://softwareengineering.stackexchange.com/questions/209099/is-it-ever-okay-to-catch-stackoverflowerror-in-java:

surely there are situations where a stack overflow might leave an application inconsistent just like a memory exhaustion. Just imagine that some object is constructed and then initialized with the help of nested internal method calls - if one of them throws, the object may very well be in a state not supposed to be possible, just as if an allocation had failed. But that doesn't mean that your solution couldn't still be the best one

E 'un motivo per essere chiamato errore e non eccezione ...

Da docs:

public abstract class VirtualMachineError extends Error: Thrown to indicate that the Java Virtual Machine is broken or has run out of resources necessary for it to continue operating

public class Error extends Throwable: An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it. A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur. That is, Error and its subclasses are regarded as unchecked exceptions for the purposes of compile-time checking of exceptions.

6

Vorrei suggerire che nella vostra Il metodo sizeRecursive si gestisce un contatore di profondità dello stack e se si supera un livello di passaggio specificato per il metodo sizeStackUsingHeap. Non fare affidamento sull'eccezione StackOverflow - questa è una cattiva pratica. Non dovresti usare eccezioni per definire il tuo algoritmo.

interface Node { 

    List<? extends Node> getSons(); 
} 

// Switch to a heap stack if the stack ever hits this level. 
private static final int STACKLIMIT = 1000; 

private static int sizeRecursive(Node root) { 
    // Start the stack depth at 0. 
    return sizeRecursive(root, 0); 
} 

// Recursive implementation. 
private static int sizeRecursive(Node root, int depth) { 
    int result = 1; 
    for (Node son : root.getSons()) { 
     if (depth < STACKLIMIT) { 
      result += sizeRecursive(son, depth + 1); 
     } else { 
      // Too deep - switch to heap. 
      result += sizeUsingHeap(son); 
     } 
    } 
    return result; 
} 

// Use this when the stack gets realy deep. It maintains the stack in the heap. 
private static int sizeUsingHeap(Node root) { 
    Stack<Node> stack = new Stack<>(); 
    stack.add(root); 
    int size = 0; 
    while (!stack.isEmpty()) { 
     // I am assuming this algorithm works. 
     Node x = stack.pop(); 
     size++; 
     for (Node son : x.getSons()) { 
      stack.push(son); 
     } 
    } 
    return size; 
} 

// Always use sizeRecursive to begin with. 
public static int size(Node root) { 
    return sizeRecursive(root); 
} 
+0

Perché è una cattiva pratica? – immibis

+0

@immibis - Per molte ragioni: 1. si sta utilizzando uno strumento per una funzione per cui non è stato progettato - 2. È piuttosto costoso creare un'eccezione in quanto è necessario creare una traccia di stack completa. Se è disponibile un algoritmo diverso (come ho postato), allora dovrebbe essere usato per massimizzare l'efficienza - vedi anche [Perché non usare le eccezioni come flusso regolare di controllo?] (Http://stackoverflow.com/questions/729379/why- non-use-eccezioni-come-regular-flow-of-control). – OldCurmudgeon

+0

se la regola è "non utilizzare le eccezioni per il controllo del flusso, arresto completo", quindi suppongo che anche la cattura di NumberFormatException sia negativa. – immibis

Problemi correlati