在 Java 中抛出异常是否昂贵?
Is throwing an Exception expensive in Java?
我有一些生成迭代器的代码,并使用 NoSuchElementException
在迭代器的任何源耗尽时发出信号。在分析这段代码时,我发现它 99% 的时间花在方法 java.util.NoSuchElementException.<init>
.
上
我知道异常在 C++ 中很昂贵,但直到现在我认为它们在 Java 中并没有那么糟糕。
使用异常进行流量控制是不好的做法。这是我很久以前写的,所以我不记得为什么我这样做了...
在 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;
}
}
}
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% 确定这是否发生在构造函数中或异常被抛出时,但以下答案表明它发生在构造函数中:
在任何情况下,正如您自己提到的,如果可以避免的话,您不应该将异常用于控制流。而且我认为应该可以重构您提供的代码来避免这种情况。
评论已经提到异常不能替代控制流。但是您不需要使用异常,因为您可以在迭代器 class 中引入 state,它包含实现 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;
}
}
}
我有一些生成迭代器的代码,并使用 NoSuchElementException
在迭代器的任何源耗尽时发出信号。在分析这段代码时,我发现它 99% 的时间花在方法 java.util.NoSuchElementException.<init>
.
我知道异常在 C++ 中很昂贵,但直到现在我认为它们在 Java 中并没有那么糟糕。
使用异常进行流量控制是不好的做法。这是我很久以前写的,所以我不记得为什么我这样做了...
在 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;
}
}
}
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% 确定这是否发生在构造函数中或异常被抛出时,但以下答案表明它发生在构造函数中:
在任何情况下,正如您自己提到的,如果可以避免的话,您不应该将异常用于控制流。而且我认为应该可以重构您提供的代码来避免这种情况。
评论已经提到异常不能替代控制流。但是您不需要使用异常,因为您可以在迭代器 class 中引入 state,它包含实现 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;
}
}
}