Java 在映射器中写入自定义对象时出错

Java 在映射器中写入自定义对象时出错,java,hadoop,mapreduce,writable,Java,Hadoop,Mapreduce,Writable,我扩展了WritableComparable,并希望将其存储为mapper作为mapper值 public class SenderRecieverPair implements WritableComparable<BinaryComparable> { Set<InternetAddress> pair = new TreeSet<InternetAddress>(new Comparator<InternetAddress>() {

我扩展了WritableComparable,并希望将其存储为mapper作为mapper值

public class SenderRecieverPair implements WritableComparable<BinaryComparable> {

    Set<InternetAddress> pair = new TreeSet<InternetAddress>(new Comparator<InternetAddress>() {

        @Override
        public int compare(InternetAddress add1, InternetAddress add2) {
            return add1.getAddress().compareToIgnoreCase(add2.getAddress());
        }

    });

    public SenderRecieverPair() {
        super();
    }

    public SenderRecieverPair(InternetAddress add1, InternetAddress add2) {
        super();
        pair.add(add1);
        pair.add(add1);
    }


    public Set<InternetAddress> getPair() {
        return pair;
    }

    @Override
    public void write(DataOutput out) throws IOException {
        for (Iterator<InternetAddress> iterator = pair.iterator(); iterator.hasNext();) {
            InternetAddress email = (InternetAddress) iterator.next();
            String mailAddress = email.getAddress();
            if(mailAddress == null) {
                mailAddress = "";
            }
            byte[] address = mailAddress.getBytes("UTF-8");
            WritableUtils.writeVInt(out, address.length);
            out.write(address, 0, address.length);
            String displayName = email.getPersonal();
            if(displayName == null) {
                displayName = "";
            }
            byte[] display = displayName.getBytes("UTF-8");
            WritableUtils.writeVInt(out, display.length);
            out.write(display, 0, display.length);
        }

    }

    @Override
    public void readFields(DataInput in) throws IOException {
        for (int i = 0; i < 2; i++) {
            int length = WritableUtils.readVInt(in);
            byte[] container = new byte[length];
            in.readFully(container, 0, length);
            String mailAddress = new String(container, "UTF-8");
            length = WritableUtils.readVInt(in);
            container = new byte[length];
            in.readFully(container, 0, length);
            String displayName = new String(container, "UTF-8");
            InternetAddress address = new InternetAddress(mailAddress, displayName);
            pair.add(address);
        }

    }

    @Override
    public int compareTo(BinaryComparable o) {
        // TODO Auto-generated method stub
        return 0;
    }

}

谢谢

java.io.EOFException如果您试图读取文件末尾以外的其他对象,将引发异常。因此,我认为,由于您在readFields方法中循环,这可能是问题背后的原因。

两个观察结果:

  • 如果您知道在
    SenderRecieverPair
    中使用的是一对,那么我不会使用集合-将这两个对象显式存储为实例变量。该集合允许您无意中向集合中添加额外的值,并且您的写入方法将根据集合大小写入0、1、2或更多值(readFields方法明确要求for循环中包含2)
  • 其次,如果您坚持使用集合,那么您应该知道hadoop在调用map/reduce任务之间会重新使用对象实例。这意味着每次调用map/reduce方法时,实际的对象引用都是相同的,只是通过调用
    readFields
    底层内容会发生变化。在您的情况下,不调用
    pair.clear()
    作为readFields方法的第一部分,这意味着在调用之间集合将继续增长
  • 最后,在
    InternetAddress
    类中使用文本对象来存储电子邮件地址和显示名称,这样序列化就简单多了,因为您可以委托对象,而对象可以委托给文本对象:
例如:

public class InternetAddress implements WritableComparable<InternetAddress> {
    protected Text emailAddress = new Text();
    protected Text displayName = new Text();

    // getter and setters for the above two fields
    // ..

    // compareTo method
    // ..

    @Override
    public void write(DataOutput out) throws IOException {
        emailAddress.write(out);
        displayName.write(out);
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        emailAddress.readFields(in);
        displayName.readFields(in);
    }
}

public class SenderRecieverPair implements WritableComparable<BinaryComparable> {
    protected Set<InternetAddress> pair = new TreeSet<InternetAddress>();

    // other methods omitted
    ..

    @Override
    public void write(DataOutput out) throws IOException {
        int safety = 0;
        for (Iterator<InternetAddress> iterator = pair.iterator(); iterator.hasNext();) {
          InternetAddress p1 = (InternetAddress) iterator.next();
          p1.write(out);

          p2 = (InternetAddress) iterator.next();
          p2.write(out);

          if (++safety == 3) {
              throw new IOException("More than two items in pair");
          }
        }
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        pair.clear();

        // Note a more efficient method would be to re-use the objects already in the set (which is even easier to do if you don't use a set and just store the two objects as instance variables)

        InternetAddress a1 = new InternetAddress();
        a1.readFields(in);
        pair.add(a1);

        InternetAddress a2 = new InternetAddress();
        a2.readFields(in);
        pair.add(a2);
    }
}
公共类InternetAddress实现可写性{
受保护的文本emailAddress=新文本();
受保护的文本显示名称=新文本();
//上述两个字段的getter和setter
// ..
//比较法
// ..
@凌驾
public void write(DataOutput out)引发IOException{
电子邮件地址。写(出);
显示名称。写入(输出);
}
@凌驾
public void readFields(DataInput in)引发IOException{
emailAddress.readFields(在中);
displayName.readFields(在中);
}
}
公共类SenderRecieverPair实现了WritableComparable{
受保护集对=新树集();
//省略了其他方法
..
@凌驾
public void write(DataOutput out)引发IOException{
内部安全=0;
for(Iterator Iterator=pair.Iterator();Iterator.hasNext();){
InternetAddress p1=(InternetAddress)迭代器.next();
p1.写(出);
p2=(InternetAddress)迭代器.next();
p2.写(出);
如果(++安全==3){
抛出新IOException(“两个以上的项成对出现”);
}
}
}
@凌驾
public void readFields(DataInput in)引发IOException{
pair.clear();
//注:一种更有效的方法是重复使用集合中已有的对象(如果不使用集合,只将两个对象存储为实例变量,则更容易做到这一点)
InternetAddress a1=新的InternetAddress();
a1.读取字段(输入);
添加(a1);
InternetAddress a2=新的InternetAddress();
a2.读取字段(输入);
添加(a2);
}
}
哦,我没有看到
hashCode
方法-如果您使用
HashPartitioner
(默认设置)并在映射器和还原器之间传递这些对象,那么肯定应该覆盖这些方法。

这是故意的吗

public SenderRecieverPair(InternetAddress add1, InternetAddress add2) {
    super();
    pair.add(add1);
    pair.add(add1);
}

您添加了两次add1,因此在写循环中,您只能从集合中获得一个元素,而不是两个

我不确定是否这样做。我正在将2个internet地址作为字节数组写入,然后一次读取2个,以构建我的地址对。我仔细检查了我的代码,它要么同时输入,要么不输入。或者有没有更好的方法来解释我在做什么嗨,克里斯,这是一个很好的解释,但我还是得到了错误。不知道是什么原因导致在映射器中调用HadoopInternetAddress.readFields(),原因是:java.io.DataInputStream.readByte(DataInputStream.java:250)处的java.io.EOFEException在HadoopInternetAddress.readFields(Text.java:263)处的WritableUtils.readVInt(WritableUtils.java:320)在HadoopInternetAddress.readFields(HadoopInternetAddress.java:41)处的在org.apache.hadoop.io.WritableComparator.compare(WritableComparator.java:122)的com.edureka.sumit.enron.datatype.HadoopSenderRecieverPair.readFields(HadoopSenderRecieverPair.java:57)上。。。13更多好的,你可以发布一个链接到你的完整类的pastebin/git gist等列表,或者用你的可写可比类的完整实现更新你的问题(你的值类是什么,这也是自定义的吗?)请找到链接基本上有一组邮件,我想计算邮件转储中的发送者-接收者对和
EnronMailMapper.class
?的源代码,并查看Amir Pauker的答案,看起来您有复制/粘贴错误
public SenderRecieverPair(InternetAddress add1, InternetAddress add2) {
    super();
    pair.add(add1);
    pair.add(add1);
}