有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在Java中抛出异常代价高昂吗?

我有一些生成迭代器的代码,并使用NoSuchElementException在迭代器的源用尽时发出信号。分析这段代码时,我发现99%的时间都花在方法java.util.NoSuchElementException.<init>

我知道C++中的异常是昂贵的,但直到现在我才认为爪哇的情况并不太坏。p>

在流控制中使用异常是不好的做法。我写这篇文章是很久以前的事了,所以我想不起来为什么我会这样做

就所需时间而言,在Java中引发异常是一项代价高昂的操作吗


以下是代码:

public abstract class SequenceIterator<E> implements Iterator<E>
{
    /** Caches the next lazily generated solution, when it has already been asked for by {@link #hasNext}. */
    private E nextSolution = null;

    /** Used to indicate that the sequence has been exhausted. */
    private boolean searchExhausted = false;

    /**
     * Generates the next element in the sequence.
     *
     * @return The next element from the sequence if one is available, or <tt>null</tt> if the sequence is complete.
     */
    public abstract E nextInSequence();

    /**
     * Checks if a sequnce has more elements, caching any generated as a result of the check.
     *
     * @return <tt>true</tt> if there are more elements, <tt>false</tt> if not.
     */
    public boolean hasNext()
    {
        boolean hasNext;

        try
        {
            nextInternal();
            hasNext = true;
        }
        catch (NoSuchElementException e)
        {
            // Exception noted so can be ignored, no such element means no more elements, so 'hasNext' is false.
            e = null;

            hasNext = false;
        }

        return hasNext;
    }

    /**
     * Gets the next element from the sequence if one is available. The difference between this method and
     * {@link #nextInSequence} is that this method consumes any cached solution, so subsequent calls advance onto
     * subsequent solutions.
     *
     * @return The next solution from the search space if one is available.
     *
     * @throws NoSuchElementException If no solutions are available.
     */
    public E next()
    {
        // Consume the next element in the sequence, if one is available.
        E result = nextInternal();
        nextSolution = null;

        return result;
    }

    /**
     * Removes from the underlying collection the last element returned by the iterator (optional operation). This
     * method can be called only once per call to <tt>next</tt>. The behavior of an iterator is unspecified if the
     * underlying collection is modified while the iteration is in progress in any way other than by calling this
     * method.
     *
     * @throws UnsupportedOperationException The <tt>remove</tt> operation is not generally supported by lazy sequences.
     */
    public void remove()
    {
        throw new UnsupportedOperationException("Lazy sequences, in general, do not support removal.");
    }

    /**
     * Gets the next element from the sequence, the cached one if one has already been generated, or creating and
     * caching a new one if not. If the cached element from a previous call has not been consumed, then subsequent calls
     * to this method will not advance the iterator.
     *
     * @return The next solution from the search space if one is available.
     *
     * @throws NoSuchElementException If no solutions are available.
     */
    private E nextInternal()
    {
        // Check if the search space is already known to be empty.
        if (searchExhausted)
        {
            throw new NoSuchElementException("Sequence exhausted.");
        }

        // Check if the next soluation has already been cached, because of a call to hasNext.
        if (nextSolution != null)
        {
            return nextSolution;
        }

        // Otherwise, generate the next solution, if possible.
        nextSolution = nextInSequence();

        // Check if the solution was null, which indicates that the search space is exhausted.
        if (nextSolution == null)
        {
            // Raise a no such element exception to signal the iterator cannot continue.
            throw new NoSuchElementException("Seqeuence exhausted.");
        }
        else
        {
            return nextSolution;
        }
    }
}

共 (2) 个答案

  1. # 1 楼答案

    评论已经提到,异常并不能取代控制流。但是您不需要使用异常,因为您可以在迭代器类中引入状态,该类包含所有必要的信息,以使用公共帮助器方法实现hasNext()next()

    例如:

    public abstract class SequenceIterator<E> implements Iterator<E> {
        private boolean searchExhausted = false;
        private E next;
        private boolean hasNext;
        private boolean nextInitialized;
    
        public abstract E nextInSequence();
    
        @Override public boolean hasNext() {
            if (!nextInitialized)
                initNext();
            return hasNext;
        }
    
        @Override public E next() {
            if (!nextInitialized)
                initNext();
            if (!hasNext())
                throw new NoSuchElementException();
            nextInitialized = false;
            return next;
        }
    
        private void initNext() {
            hasNext = false;
            nextInitialized = true;
    
            if (!searchExhausted) {
                next = nextInSequence();
                hasNext = next != null;
            }
        }
    }
    
  2. # 2 楼答案

    Its bad practice to use exceptions for flow control, but there were other reasons that I was in some way compelled to do this - the Iterator interface does not provide a way of returning a special value to indicate "no more values left".

    https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#hasNext

    Is throwing an Exception in Java an expensive operation, in terms of the time it takes?

    AFAIK,尤其是为异常创建堆栈跟踪非常昂贵。我不能100%确定这是在构造函数中发生还是在抛出异常时发生,但下面的答案表明它发生在构造函数中:https://stackoverflow.com/a/8024032/506855

    在任何情况下,正如您自己提到的,如果可以避免的话,您不应该对控制流使用异常。我认为应该可以重构您提供的代码来避免这种情况