Normalement, nous écrivons le mappeur sous la forme:
public static class Map extends Mapper<**LongWritable**, Text, Text, IntWritable>
Ici, la paire clé-valeur en entrée pour le mappeur est <LongWritable, Text>
- autant que je sache lorsque le mappeur obtient les données d'entrée, il passe ligne par ligne - de sorte que la clé du mappeur indique le numéro de ligne - corrigez-moi si je me trompe. .
Ma question est la suivante: si je donne la paire clé-valeur d'entrée pour le mappeur sous la forme <Text, Text>
, elle donne alors l'erreur
Java.lang.ClassCastException: org.Apache.hadoop.io.LongWritable cannot be cast to org.Apache.hadoop.io.Text
Est-il obligatoire de donner la paire clé-valeur d'entrée du mappeur sous la forme <LongWritable, Text>
- si oui, pourquoi? si non alors quelle est la raison de l'erreur? Pouvez-vous m'aider à comprendre le bon raisonnement de l'erreur?
Merci d'avance.
L'entrée dans le mappeur dépend de ce que InputFormat est utilisé. InputFormat est responsable de la lecture des données entrantes et de leur mise en forme selon le format attendu par le mappeur. L'entrée par défautFormat est TextInputFormat , qui étend FileInputFormat<LongWritable, Text>
.
Si vous ne modifiez pas InputFormat, l'utilisation d'un mappeur avec une signature de type clé-valeur différente de celle de <LongWritable, Text>
provoquera cette erreur. Si vous attendez <Text, Text>
entrée, vous devrez choisir un InputFormat approprié. Vous pouvez définir InputFormat dans la configuration du travail:
job.setInputFormatClass(MyInputFormat.class);
Et comme je l'ai dit, par défaut, cela est défini sur TextInputFormat.
Supposons maintenant que vos données d'entrée sont constituées d'un ensemble d'enregistrements séparés par une nouvelle ligne, délimités par une virgule:
Si vous voulez que la clé d'entrée du mappeur soit ("A", "valeur1"), ("B", "valeur2"), vous devrez implémenter un InputFormat et un RecordReader personnalisés avec la signature <Text, Text>
. Heureusement , c'est assez facile. Il y a un exemple ici et probablement quelques exemples flottant autour de StackOverflow également.
En bref, ajoutez une classe qui étend FileInputFormat<Text, Text>
et une classe qui étend RecordReader<Text, Text>
. Remplacez la méthode FileInputFormat#getRecordReader
et demandez-lui de renvoyer une instance de votre RecordReader personnalisé.
Ensuite, vous devrez implémenter la logique RecordReader requise. La manière la plus simple de procéder consiste à créer une instance de LineRecordReader dans votre RecordReader personnalisé et à déléguer toutes les responsabilités de base à cette instance. Dans les méthodes getCurrentKey et getCurrentValue, vous allez implémenter la logique d'extraction du contenu texte délimité par des virgules en appelant LineRecordReader#getCurrentValue
et en le divisant par virgule.
Enfin, définissez votre nouveau InputFormat comme Job InputFormat comme indiqué après le deuxième paragraphe ci-dessus.
Dans le livre "Hadoop: The Difinitive Guide" de Tom White, je pense qu'il a une réponse appropriée à cela (p. 197):
"Les touches De TextInputFormat, qui constituent simplement le décalage dans le fichier, ne sont normalement pas très utiles. Il est courant que chaque ligne d'un fichier soit une paire clé-valeur, séparée par un délimiteur , par exemple un caractère de tabulation. Par exemple, il s’agit de la sortie produite par TextOutputFormat, la valeur par défaut de Hadoop. Pour interpréter correctement ces fichiers, KeyValueTextInputFormat [.____. .] est approprié.
Vous pouvez spécifier le séparateur via la propriété Key.value.separator.in.input.line . Est un caractère de tabulation par défaut. "