FileDataModel在官方说是除了装载csv,tsv文件外,还可以装载压缩文件例如zip或gzip的,mahout in action书中也是那样说的。于是我抱着学习的态度去试验了一把,结果是出人意料的报错了:
Exception in thread "main" java.util.NoSuchElementException
at com.google.common.collect.AbstractIterator.peek(AbstractIterator.java:169)
at org.apache.mahout.cf.taste.impl.model.file.FileDataModel.<init>(FileDataModel.java:193)
at org.apache.mahout.cf.taste.impl.model.file.FileDataModel.<init>(FileDataModel.java:169)
at org.apache.mahout.cf.taste.impl.model.file.FileDataModel.<init>(FileDataModel.java:149)
at mahout.TestRecommenderEvaluator.main(TestRecommenderEvaluator.java:26)
于是下载了源码打开去看看到底是什么情况,也是发现了问题,改了改代码:FileLineIterator这个类中的getFileInputStream方法
static InputStream getFileInputStream(File file) throws IOException {
InputStream is = new FileInputStream(file);
String name = file.getName();
if ("gz".equalsIgnoreCase(Files.getFileExtension(name.toLowerCase()))) {
return new GZIPInputStream(is);
} else if ("zip".equalsIgnoreCase(Files.getFileExtension(name
.toLowerCase()))) {
//这是我改过的
ZipFile zf = null;
ZipInputStream zis = null;
try {
zf = new ZipFile(file);
zis = new ZipInputStream(is);
ZipEntry entry = zis.getNextEntry();
if (entry == null) {
throw new IOException("空的zip压缩文件,无法获得偏好值文件");
}
return zf.getInputStream(entry);
} finally {
if (zis != null) {
zis.close();
}
}
//这是源码中的写法
//return new ZipInputStream(is);
} else {
return is;
}
}
源码中只是返回了ZipInputStream,而不是返回zip文件中某个具体的entry的stream。所以到外层调用String firstLine = iterator.peek();会报错。
我把源码中FileLineIterator的代码全部考了出来,在项目中按照全路径建了一个一样的类,然后修改代码,可以覆盖掉JAR包中的这个类。
现在我们可以测试一下装载zip文件了,写个main方法,添加一下代码:
DataModel dataModel = new FileDataModel(new File(
MyFirstRecommender.class.getResource("intro.zip").getPath()));
FastIDSet items = dataModel.getItemIDsFromUser(1);
System.out.println(items);
可以正确的装载并打印出userid=1的所有items。
最后我也思考了一下,为什么这个问题官方都没有修复,可能是因为实际当中根本就不会去装载zip文件,zip文件只是减少了一些磁盘的开销,虽然对于偏好值这种文本文件压缩比例非常高,但是并不能减少装载偏好值文件的内存的开销,因为后面还是要把zip文件在内存中解压然后读取里面的entry,返到由于解压zip消耗了更宝贵的cpu资源。
因为目前还没有实际经验,所以只是一个个人的看法,有误也请留言指正