Iterable接口的模糊性及其Java实现

Iterable接口的模糊性及其Java实现,java,hadoop,mapreduce,java-stream,iterable,Java,Hadoop,Mapreduce,Java Stream,Iterable,当我们在这个接口上调用next()方法时,JavaIterator接口是否强制我们返回一个新对象?我查阅了文档,没有义务在每次调用时返回一个新对象,但这会导致许多歧义。Hadoop mapreduce框架似乎打破了一些未记录的规则,这在我的简单程序(包括使用Java8流)中造成了许多问题。当我在迭代器上调用next()方法时,它返回相同的对象,但内容不同(尽管这与我的想象相反,但它似乎没有打破迭代器的规则,至少它似乎没有打破迭代器接口的成文规则)。我想知道为什么会这样?这是mapreduce故障

当我们在这个接口上调用
next()
方法时,Java
Iterator
接口是否强制我们返回一个新对象?我查阅了文档,没有义务在每次调用时返回一个新对象,但这会导致许多歧义。Hadoop mapreduce框架似乎打破了一些未记录的规则,这在我的简单程序(包括使用Java8流)中造成了许多问题。当我在
迭代器上调用
next()
方法时,它返回相同的对象,但内容不同(尽管这与我的想象相反,但它似乎没有打破
迭代器的规则,至少它似乎没有打破
迭代器
接口的成文规则)。我想知道为什么会这样?这是mapreduce故障吗?还是因为Java没有记录每次调用
next()
方法时返回新实例的
Iterator
接口

为了简单起见,并展示hadoop mapreduce中正在发生的事情,我编写了自己的
迭代器
,它与mapreduce的工作类似,因此您可以理解我的意思(因此它不是一个完美的程序,可能有很多问题,但请关注我试图展示的概念)。 假设我有以下
医院
实体:

@Getter
@Setter
@AllArgsConstructor
@ToString
public class Hospital {
    private AREA area;
    private int patients;

    public Hospital(AREA area, int patients) {
        this.area = area;
        this.patients = patients;
    }

    public Hospital() {
    }
}
为此,我编写了以下
mycustomhospitalable

public class MyCustomHospitalIterable implements Iterable<Hospital> {

    private List<Hospital> internalList;
    private CustomHospitalIteration customIteration = new CustomHospitalIteration();

    public MyCustomHospitalIterable(List<Hospital> internalList) {
        this.internalList = internalList;
    }

    @Override
    public Iterator<Hospital> iterator() {
        return customIteration;
    }

    public class CustomHospitalIteration implements Iterator<Hospital> {
        private int currentIndex = 0;
        private Hospital currentHospital = new Hospital();

        @Override
        public boolean hasNext() {

            if (MyCustomHospitalIterable.this.internalList.size() - 1 > currentIndex) {
                currentIndex++;
                return true;
            }
            return false;
        }

        @Override
        public Hospital next() {
            Hospital hospital =
                    MyCustomHospitalIterable.this.internalList.get(currentIndex);
            currentHospital.setArea(hospital.getArea());
            currentHospital.setPatients(hospital.getPatients());
            return currentHospital;
        }
    }
}
这是如此不合逻辑和违反直觉,以至于程序的输出如下所示:

Hospital{area=AREA2, patients=20}
Hospital{area=AREA3, patients=30}
Hospital{area=AREA1, patients=40}
---------------------
[Hospital{area=AREA1, patients=40}, Hospital{area=AREA1, patients=40}, Hospital{area=AREA1, patients=40}]
{AREA2=20, AREA1=40, AREA3=30}
更糟糕的是,想象一下当我们使用Java中的
流时会发生什么。以下Java程序的输出是什么:

public static void main(String[] args) {
        List<Hospital> hospitalArray = Arrays.asList(
                new Hospital(AREA.AREA1, 10),
                new Hospital(AREA.AREA2, 20),
                new Hospital(AREA.AREA3, 30),
                new Hospital(AREA.AREA1, 40));
        MyCustomHospitalIterable hospitalIterable = new MyCustomHospitalIterable(hospitalArray);
        Map<AREA, Integer> sortedHospital =
                StreamSupport.stream(hospitalIterable.spliterator(), false)
                        .collect(Collectors.groupingBy(
                                Hospital::getArea, Collectors.summingInt(Hospital::getPatients)));
        System.out.println(sortedHospital);
}
与之平行的是:

{AREA1=120}
作为一个用户,我希望按原样使用界面,而不必担心该界面的实现

问题是这里我知道如何实现
mycustomhospitalable
,但在hadoop mapreduce中,我必须实现像bellow这样的方法,我不知道
Iterable
从哪里来,它的实现是什么。我只想将其用作纯
Iterable
接口,但正如我在上面所展示的,它并没有按预期工作:

public void reduce(Text key, Iterable<IntWritable> values, Context context
        ) throws IOException, InterruptedException {
            List<IntWritable> list = new LinkedList<>();
            Iterator<IntWritable> iter = values.iterator();
            while (iter.hasNext()) {
                IntWritable count = iter.next();
                System.out.println(count);
                list.add(count);
            }
            System.out.println("---------------------");
            System.out.println(list);
}
public void reduce(文本键、Iterable值、上下文
)抛出IOException、InterruptedException{
列表=新建LinkedList();
迭代器iter=values.Iterator();
while(iter.hasNext()){
IntWritable count=iter.next();
系统输出打印项次(计数);
列表。添加(计数);
}
System.out.println(“--------------------------”;
系统输出打印项次(列表);
}
我的问题是: 为什么我的简单程序坏了

  • 不执行
    Iterable
    Iterator
    的未经证实的常规规则是mapreduce的错误吗(或者我没有注意到关于这种行为的文档)
  • 还是Java没有记录
    Iterable
    Iterator
    接口以在每次调用时返回新对象
  • 还是因为我是个程序员

  • 对于Iterable,返回相同的可变对象并包含不同的内容是非常不寻常的。我在java语言参考中没有找到任何东西;虽然搜索不多。它很简单,太容易出错,很容易被正确的语言使用

    您提到的其他工具,如Streams,都是合适的

    另外,下一个java的记录类型只是用于类似元组的用法,当然是作为多个不可变对象。“您的”Iterable无法在集合中使用,除非on执行
    .next().clone()
    或类似操作


    Iterable的这一弱点与将可变对象作为映射键属于同一类。这是一个致命的错误。

    谢谢你的回答,但我遇到了这个问题,需要花时间去理解幕后发生的事情。我刚刚添加了在mapreduce中必须使用的reduce方法。我想知道谁做错了什么?JAVA因为他们没有很好地记录它,或者因为他们没有遵守规则而减少,哪条规则?或者这是我的错?在我的想象中,迭代器也不应该返回同一个对象,但是在哪里有文档记录可以用来指责hadoop为这个Iterable实现@Joop Eggen可能是一些hadoop开发人员“优化”了他们的Iterable,因为他们没有创建多个对象。(但是Java可以处理这个问题。)他们的javadoc应该包含一个明确的警告。这个问题更适合hadoop论坛。也许最好相应地标记这个问题,让hadoop的人知道。Iterable的规范中没有任何东西,因为它不需要在那里。修改可能仍在某处使用的对象以表示不同的对象是一种基本的设计风格。恰恰相反。如果不允许使用迭代器的代码将对象存储在某个位置,在下一次调用
    next()
    后使用它,接口需要一个文档。@Tashkhishi您能举出任何例子来说明存在不符合规范的集合实现吗?当然,如果hadoop表现出这样的行为,他们应该做的最低限度就是记录它和它施加的限制。请,下次你问什么问题时,多注意语法和拼写。我希望你能接受我的道歉@GiorgiTsiklauri
    public void reduce(Text key, Iterable<IntWritable> values, Context context
            ) throws IOException, InterruptedException {
                List<IntWritable> list = new LinkedList<>();
                Iterator<IntWritable> iter = values.iterator();
                while (iter.hasNext()) {
                    IntWritable count = iter.next();
                    System.out.println(count);
                    list.add(count);
                }
                System.out.println("---------------------");
                System.out.println(list);
    }