Iterable接口中的hadoop模糊性及其Java实现
当我们在这个接口上调用next()
方法时,Java Iterator
接口是否强制我们返回一个新对象?我查阅了文档,没有义务在每次调用时返回一个新对象,但这会导致许多歧义。Hadoop mapreduce框架似乎打破了一些未记录的规则,这在我的简单程序(包括使用Java8流)中造成了许多问题。当我在Iterator
上调用next()
方法时,它返回具有不同内容的同一个对象(尽管这违背了我的想象,但它似乎没有打破Iterator
的规则,至少它似乎没有打破Iterator
接口的文档化规则)。我想知道为什么会这样?这是mapreduce故障吗?还是因为Java没有记录Iterator
接口,在每次调用next()
方法时都返回新实例,这是错误的:
为了简单起见,并展示hadoop mapreduce中正在发生的事情,我编写了自己的Iterator
,这与mapreduce的工作类似,因此您可以理解我的意思(因此它不是一个完美的程序,可能有很多问题,但请关注我试图展示的概念)。
假设我有以下Hospital
实体:
@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() {
}
}
为此,我写了以下内容MyCustomHospitalIterable
:
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;
}
}
}
在这里,我不是在next()方法调用时返回新对象,而是返回具有不同内容的相同对象。你可能会问这样做的好处是什么?它在mapreduce中有自己的优势,因为在大数据中,他们不想为了性能考虑而创建新对象。这是否违反了Iterator
接口的任何成文规则
现在让我们看看以这种方式实现Iterable
的一些后果:
考虑以下简单程序:
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);
List<Hospital> hospitalList = new LinkedList<>();
Iterator<Hospital> hospitalIter = hospitalIterable.iterator();
while (hospitalIter.hasNext()) {
Hospital hospital = hospitalIter.next();
System.out.println(hospital);
hospitalList.add(hospital);
}
System.out.println("---------------------");
System.out.println(hospitalList);
}
这是如此不合逻辑和违反直觉,以至于程序的输出如下所示:
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}]
更糟糕的是,想象一下当我们使用Java中的Streams
时会发生什么。以下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);
}
这取决于我们使用并行流或顺序流: 在Sequentioal中,一个输出如下:
{AREA2=20, AREA1=40, AREA3=30}
与之平行的是:
{AREA1=120}
作为一个用户,我希望按原样使用界面,而不必担心该界面的实现
问题是,这里我知道MyCustomHospitalIterable
是如何实现的,但在hadoop mapreduce中,我必须实现像bellow这样的方法,我不知道Iterable<IntWritable>
来自哪里,它的实现是什么。我只想将其用作纯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);
}
我的问题是: 为什么我的简单程序坏了
- 不执行
Iterable
和Iterator
的未经证实的常规规则是mapreduce的错吗(或者我没有注意到关于这种行为的文档)李> - 还是Java没有记录
Iterable
和Iterator
接口以在每次调用时返回新对象李> - 还是因为我是个程序员李>
# 1 楼答案
对于Iterable,返回相同的可变对象并包含不同的内容是非常不寻常的。我在java语言参考中没有找到任何东西;虽然搜索不多。它很简单,太容易出错,很容易被正确的语言使用
您提到的其他工具,如Streams,都是合适的
另外,下一个java的记录类型只用于类似元组的用法,当然是作为多个不可变对象。“Your”Iterable无法在集合中使用,除非on执行
.next().clone()
之类的操作Iterable的这一弱点与将可变对象作为映射键属于同一类<这是大错特错