#hadoop #mapreduce #writable
#hadoop #mapreduce создать #доступный для записи
Вопрос:
Я пытаюсь написать программу сокращения карты, которая проверяет наличие общих друзей. Я использую пользовательский объект для записи (FriendPair) в качестве ключа.
Учитывая следующие входные данные
Tom Jerry,John
John Jerry,Sarah,Tom
Он должен выводить Джерри как общего друга для Тома и Джона
[John,Tom] Jerry
[John,Sarah]
[John,Jerry]
[Tom,Jerry]
Вместо этого map reduce выводит следующее
[John,Tom]
[John,Sarah]
[John,Jerry]
[Tom,John]
[Tom,Jerry]
Ключи [John, Tom] и [Tom,John] считаются неравными.
Ниже приведен код
Настраиваемый записываемый
public class FriendPair implements WritableComparable<FriendPair> {
Text friend1;
Text friend2;
public FriendPair() {
this.friend1 = new Text("");
this.friend2 = new Text("");
}
public FriendPair(Text friend1, Text friend2) {
this.friend1 = friend1;
this.friend2 = friend2;
}
public Text getFriend1() {
return friend1;
}
public void setFriend1(Text friend1) {
this.friend1 = friend1;
}
public Text getFriend2() {
return friend2;
}
public void setFriend2(Text friend2) {
this.friend2 = friend2;
}
@Override
public void write(DataOutput out) throws IOException {
friend1.write(out);
friend2.write(out);
}
@Override
public void readFields(DataInput in) throws IOException {
friend1.readFields(in);
friend2.readFields(in);
}
@Override
public int compareTo(FriendPair pair2) {
return ((friend1.compareTo(pair2.getFriend2()) == 0 amp;amp; friend2.compareTo(pair2.getFriend1()) == 0)
|| (friend1.compareTo(pair2.getFriend1()) == 0 amp;amp; friend2.compareTo(pair2.getFriend2()) == 0)) ? 0 : -1;
}
@Override
public boolean equals(Object o) {
FriendPair pair2 = (FriendPair) o;
return (friend1.equals(pair2.getFriend2()) amp;amp; friend2.equals(pair2.getFriend1())
|| friend1.equals(pair2.getFriend1()) amp;amp; friend2.equals(pair2.getFriend2()));
}
@Override
public String toString() {
return "[" friend1 "," friend2 "]";
}
@Override
public int hashCode() {
return friend1.hashCode() friend2.hashCode();
}
}
Mapper
public class MutualFriendsMapper extends Mapper<LongWritable, Text, FriendPair, Text> {
@Override
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] items = line.split("t");
String name = items[0];
String friendsList = items[1];
String[] friends = friendsList.split(",");
for (String friend : friends) {
FriendPair fp = new FriendPair(new Text(name), new Text(friend));
FriendPair fp2 = new FriendPair(new Text(friend), new Text(name));
context.write(fp, new Text(friendsList));
}
}
}
Редуктор
public class MutualFriendsReducer extends Reducer<FriendPair, Text, FriendPair, FriendArray> {
@Override
public void reduce(FriendPair key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
List<String> allFriends = new ArrayList<String>();
for(Text value : values) {
String[] valueArray = value.toString().split(",");
allFriends.addAll(Arrays.asList(valueArray));
}
List<Text> commonFriends = new ArrayList<Text>();
Set<String> uniqueFriendSet = new HashSet<String>(allFriends);
for(String friend : uniqueFriendSet) {
int frequency = Collections.frequency(allFriends, friend);
if(frequency > 1) {
commonFriends.add(new Text(friend));
}
}
context.write(key, new FriendArray(Text.class, commonFriends.toArray(new Text[commonFriends.size()])));
}
}
FriendArray (Вывод)
public class FriendArray extends ArrayWritable {
public FriendArray(Class<? extends Writable> valueClass, Writable[] values) {
super(valueClass, values);
}
public FriendArray(Class<? extends Writable> valueClass) {
super(valueClass);
}
public FriendArray() {
super(Text.class);
}
@Override
public Text[] get() {
return (Text[]) super.get();
}
@Override
public void write(DataOutput data) throws IOException {
for(Text t : get()) {
t.write(data);
}
}
@Override
public String toString() {
Text[] friendArray = Arrays.copyOf(get(), get().length, Text[].class);
String print="";
for(Text f : friendArray)
print =f ",";
return print;
}
}
Любая помощь будет с благодарностью принята.
Ответ №1:
На этапе «сортировки» Hadoop не работает с объектами Java, а только с их байтовым представлением (вывод FriendPair.write()
метода), поэтому он не может вызвать FriendPair.equals()
. Итак, чтобы заставить Hadoop понять, что ключи [John, Tom] и [Tom,John] равны, вы должны убедиться, что их write
выходные данные идентичны. Один из способов добиться этого — обеспечить соблюдение порядка друзей в паре, например, отсортировать их по алфавиту (тогда обе пары будут выглядеть [John, Tom] ).
Комментарии:
1. Это сработало, но я до сих пор не понимаю, почему метод compareTo не возвращал 0, когда пары [Tom, John] и [John, Tom] равны. Разве они не должны быть отправлены в один и тот же редуктор?
2. Что ж, вы частично правы. Они отправляются в один и тот же экземпляр редуктора, потому что у них одинаковый хэш-код. Но редуктор видит их как разные ключи, потому что их байтовое представление отличается.