Java 一种迭代器,它变异并返回相同的对象。坏习惯?

Java 一种迭代器,它变异并返回相同的对象。坏习惯?,java,iterator,guava,Java,Iterator,Guava,我正在编写GC友好的代码来读取并向用户返回一系列byte[]消息。在内部,我重复使用相同的实例,这意味着我将在大多数情况下重复返回相同的byte[]实例 我正在考虑编写警告性的javadoc,并将其作为一个示例公开给用户。当然,它不会违反迭代器契约,但是如果用户违反了契约,并且在每个位置都会得到一个列表,其中填充了相同的字节[] 问题是:对于一个类来说,可能会变异并返回相同的对象来实现迭代器接口,这是否是一种不良做法 如果是,最好的选择是什么?“不要变异/重用对象”是一个简单的答案。但它没有解

我正在编写GC友好的代码来读取并向用户返回一系列
byte[]
消息。在内部,我重复使用相同的实例,这意味着我将在大多数情况下重复返回相同的
byte[]
实例

我正在考虑编写警告性的javadoc,并将其作为一个示例公开给用户。当然,它不会违反
迭代器
契约,但是如果用户违反了契约,并且在每个位置都会得到一个
列表
,其中填充了相同的
字节[]

问题是:对于一个类来说,可能会变异并返回相同的对象来实现
迭代器
接口,这是否是一种不良做法

  • 如果是,最好的选择是什么?“不要变异/重用对象”是一个简单的答案。但它没有解决重用非常理想的情况

  • 如果没有,您如何证明违反该规则是正当的

两个小调:

  • 我用的是番石榴,所以remove()不太重要

  • 在我的用例中,用户是我,这个类的可视性将受到限制,但我已经试着从总体上问这个问题,以便更广泛地应用


更新:我接受路易斯的答案,因为它的票数是基思的3倍,但请注意,在我的用例中,我计划将我在基思答案评论中留下的代码用于生产。

EnumMap
在其
entrySet()
迭代器中基本上就是这样做的,这会导致混乱、疯狂,令人沮丧的虫子直到今天


如果我是你,我就不会使用
迭代器
——我会编写一个不同的API(甚至可能与迭代器完全不同)并实现它。例如,您可以编写一个新的API,将
ByteBuffer
作为输入,将消息写入其中,这样API的用户就可以控制缓冲区是否得到重用。这似乎是相当直观的(用户可以编写明显且干净地重用ByteBuffer的代码),而无需创建不必要的混乱代码。

我将定义一个可以使其失效的中间对象。因此,您的函数将返回一个
迭代器
,而
ByteArray
是这样的:

class ByteArray {
    private byte[] data;
    ByteArray(byte[] d) { data = d; }
    byte[] getData() {
        if (data == null) throw new BadUseOfIteratorException();
        return data;
    }
    void invalidate() { data = null; }
}
然后,迭代器可以使先前返回的
ByteArray
无效,以便将来的任何访问(通过
getData
或您提供的任何其他访问器)都将失败。然后,至少如果有人执行类似于Lists.newArrayList(myIterator)的操作,他们至少会得到一个错误(当访问第一个无效的
ByteArray
时),而不是静静地返回错误的数据

当然,这不会涵盖所有可能的不良用途,但可能是常见用途。如果您对从不返回原始的
byte[]
并提供诸如
byte get(int idx)
之类的访问器感到满意,那么它应该能够捕获所有情况


您必须为每个迭代器返回分配一个新的
ByteArray
,但希望这比为每个迭代器返回复制
byte[]
便宜得多。

就像Keith Randall一样,我也创建了
迭代器,但工作方式完全不同(下面的注释来自):

缓冲区得到重用。除非错误地调用
allowReuse()
,否则不会造成任何伤害。如果您忘记调用它,那么您的性能会变差,但行为会正确



现在我看到它可以在没有
ByteArray
的情况下工作,重要的是
myByteArrayIterable.allowReuse()
可以直接调用。

好主意!我以前没见过。很高兴听到你对我的表现的批评:和伟大的指针。我开始使用非迭代器impl,但它刚刚变成。。。一个伪装成悲伤的迭代器:)所以我尝试了基思的建议。如果你愿意的话,看看我在他的答案评论中留下的impl/test,看看它是否仍然感觉危险。哇,我已经完全忘记EnumMap了。谢谢,现在副作用的噩梦会让我在本周剩下的时间里熬夜!;-)关于“bugs to this day”方面的好消息:这个行为在Java7中得到了修复。我喜欢你对它的理解,让用户决定何时重用。我为Keith的想法写了一个impl,它也有你的想法,除了用户在创建迭代器时指定重用,而不是在使用迭代器时指定重用。具体检查链接测试文件中的“objectPool”测试,查看用户如何指定重用池的大小。干杯。@布赖恩·哈里斯:我想保守一点。。。你必须一次又一次地说“拿着这些数据,我不在乎了”,否则这些数据就不会被回收。我曾经做过这样的事情,喜欢这样,但我觉得没有必要。
@RequiredArgsConstructor
public class ByteArray {
    @Getter private final byte[] data;
    private final ByteArrayIterable source;
    void allowReuse() {
        source.allowReuse();
    }
}

public class ByteArrayIterable implements Iterable<ByteArray> {
    private boolean allowReuse;
    public allowReuse() {
        allowReuse = true;
    }
    public Iterator<ByteArray> iterator() {
        return new AbstractIterator<ByteArray>() {
            private ByteArray nextElement;
            public ByteArray computeNext() {
                if (noMoreElements()) return endOfData();
                if (!allowReuse) nextElement =
                    new ByteArray(new byte[length], ByteArrayIterable.this);
                allowReuse = false;
                fillWithNewData(lastElement.getData());
            }
        }
    }
}
for (ByteArray a : myByteArrayIterable) {
    a.allowReuse();
    process(a.getData());
}