RawComparator

本文详细解释了RawComparator如何用于比较Writable对象,包括其与WritableComparable和WritableComparator的关系,通过实验展示了如何使用RawComparator进行字节方式的比较,并深入探讨了Text类在序列化时的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RawComparator用于 Writable对象的比较,

例如:

Job.setSortComparatorClass(Class <? extends RowComparator>);
Job.setGroupingComparatorClass(Class <? extends RowComparator>);

 

 

能作为Key的 Writable有以下特征:

 必须实现 接口WritableComparable;

 一般都包含一个扩展自WritableComparator  的比较器类。

 

而 WritableComparator类,实现了 RawComparator接口。

 

public interface WritableComparable<T> extends Writable, Comparable<T>;

public interface RawComparator<T> extends Comparator<T>;

public class WritableComparator implements RawComparator;

 

 

说明其中一个方法:

public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2);

该方法以字节方式比较两个Writable对象

 

做个实验,

import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

...
private static final Logger log = LoggerFactory.getLogger(...class);

public static void main (String[] args) {
	Text text = new Text(
		"01234567890123456789012345678901234567890123456789"
		+ "01234567890123456789012345678901234567890123456789"
		+ "01234567890123456789012345678901234567890123456789"
		+ "01234567890123456789012345678901234567890123456789"
		+ "01234567890123456789012345678901234567890123456789"
		+ "01234567890123456789012345678901234567890123456789");

	/*
	CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder()
				.onMalformedInput(CodingErrorAction.REPORT)
				.onUnmappableCharacter(CodingErrorAction.REPORT);
	CharBuffer charBuffer = CharBuffer.wrap(text.toString().toCharArray());
	ByteBuffer byteBuffer = encoder.encode(charBuffer);
	int l1 = byteBuffer.limit();

	byte[] byteArray = byteBuffer.array();
	DataOutputBuffer out = new DataOutputBuffer();
	WritableUtils.writeVInt(out, l1);
	out.write(byteArray, 0, l1);
	out.close();
	byte[] b1 = out.getData();
    */
	int l1 = text.toString().length();
	byte[] b1 = WritableUtils.toByteArray(text);

	int s1 = 0;
	int n1 = WritableUtils.decodeVIntSize(b1[s1]);

	log.info("[{}, {}]", l1, n1);

	byte[] b2 = Arrays.copyOfRange(b1, s1 + n1, l1 + n1);
	log.info(new String(b2));
}

 

执行结果,

[303, 3]
012345678901234567890123456789012345678901...

 

Text 会在序列化的时候,在字节数组的最开始,标示字符串的实际长度。上例中的注释部分

class Text:
public void write(DataOutput out) throws IOException {
	WritableUtils.writeVInt(out, length);
	out.write(bytes, 0, length);
}
 

 

RawComparator comparator = new RawComparator<Text> {

	public int compare(Text t1, Text t2) { 
		return t1.toString.compareTo(t2.toString());
	}

	public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
		int n1 = WritableUtils.decodeVIntSize(b1[s1]);
		int n2 = WritableUtils.decodeVIntSize(b2[s2]);

		// Text的比较是这么实现的 
		// WritableComparator.compareBytes(b1, s1 + n1, l1 - n1, b2, s2 + n2, l2 - n2);

		// 其实完全可以这么干
		byte[] _b1 = Arrays.copyOfRange(b1, s1 + n1, s1 + l1);
		byte[] _b2 = Arrays.copyOfRange(b2, s2 + n2, s2 + l2);
		String t1 = new String(_b1);
		String t2 = new String(_b2);
		return compare(new Text(t1), new Text(t2));
	}

}
<think>好的,我需要回答用户关于MapReduce中排序函数的问题,并说明它们的区别。首先,我得回忆一下MapReduce的工作流程,以及其中涉及到的排序阶段。 在MapReduce中,排序主要发生在两个阶段:Map阶段的排序和Shuffle阶段的排序,还有可能涉及Reduce阶段的排序。不过用户问的是排序函数,可能指的是Partitioner、Combiner、以及自定义的Comparator等。需要理清这些概念。 首先,Map阶段结束后,输出的键值对会根据键进行排序,这里使用的是默认的排序方法,比如字典序,但用户也可以自定义排序器。这时候可能用到的是RawComparator,它允许更高效的比较,不需要反序列化对象。 接下来是Partitioner,它的作用是将Map的输出分发到不同的Reduce任务中,但严格来说,Partitioner并不进行排序,而是决定数据的分区。不过不同的Partitioner可能会影响数据的分布,但用户可能将分区和排序混淆了。 然后是Shuffle阶段,数据从Map任务传输到Reduce任务时,会进行归并排序,确保每个Reduce任务的输入是按键有序的。这里归并排序可能使用不同的算法,比如默认的归并策略,但用户是否可以自定义?可能需要查证。 另外,用户可能提到的排序函数包括: 1. 默认的排序(基于键的compareTo方法) 2. 自定义的Comparator,通过JobConf设置 3. 使用RawComparator进行优化 4. 在Reduce阶段对分组进行排序时的GroupingComparator 比如,当处理二次排序时,用户需要自定义Key的Comparator,同时可能还需要GroupingComparator来指定如何分组。这时候不同的Comparator分别作用于排序和分组阶段。 我需要明确各个排序函数的用途和区别。比如,默认排序是自然顺序,而自定义Comparator可以改变排序规则。RawComparator则直接比较字节流,效率更高。GroupingComparator影响分组的键比较,但排序还是由Key的Comparator决定。 可能需要分点说明每个排序函数的作用阶段、如何配置、以及它们的区别。比如: 1. 默认键排序:使用键的compareTo方法,要求键实现WritableComparable接口。 2. 自定义键排序:通过setSortComparatorClass设置,覆盖默认排序。 3. RawComparator:优化性能,直接操作字节。 4. GroupingComparator:用于决定哪些键属于同一组,不影响排序顺序,只影响分组。 另外,Combiner其实是一个本地Reduce,不会直接影响排序,而Partitioner影响分区但非排序。需要确保不混淆这些概念。 总结,需要分点列出MapReduce中的排序相关函数,解释每个的作用和区别,可能包括默认排序、自定义排序Comparator、RawComparator、GroupingComparator等。同时强调它们应用的阶段和配置方法,以及性能优化的考虑。</think>在MapReduce框架中,排序是实现高效数据处理的核心机制之一。以下是关键排序函数及其区别的逐步解析: --- ### 1. **默认键排序(自然排序)** - **作用阶段**: Map输出到Reduce输入的Shuffle阶段 - **机制**: 默认使用键的`compareTo()`方法排序,要求键必须实现`WritableComparable`接口(如`Text`、`IntWritable`等内置类型)。 - **示例**: - 数值类型按键大小升序排列。 - 字符串类型按字典序排序。 - **配置方式**: 无需额外配置,自动生效。 --- ### 2. **自定义键排序(Sort Comparator)** - **作用阶段**: Shuffle阶段的排序过程 - **机制**: 通过`job.setSortComparatorClass()`指定自定义比较器,覆盖默认排序规则。常用于复杂键(如复合键)或特殊排序需求(如降序)。 - **示例**: ```java job.setSortComparatorClass(MyKeyComparator.class); // 自定义比较逻辑 ``` - **与默认排序区别**: 完全控制排序规则,例如对复合键`(Year, Temperature)`按年份升序、温度降序排列。 --- ### 3. **RawComparator(原生比较器)** - **作用阶段**: Shuffle阶段的排序和归并 - **机制**: 直接操作序列化后的字节流,避免反序列化开销,提升性能。需实现`compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)`方法。 - **示例**: Hadoop内置类型(如`IntWritable`)默认使用优化的`RawComparator`。 - **与普通Comparator区别**: 高效但实现复杂,适用于对性能要求极高的场景。 --- ### 4. **分组排序(Grouping Comparator)** - **作用阶段**: Reduce阶段的分组过程 - **机制**: 通过`job.setGroupingComparatorClass()`定义哪些键视为同一组。**不影响整体排序顺序**,仅决定哪些记录被分到同一Reduce调用。 - **示例**: 对复合键`(Year, Month)`,若按`Year`分组,则同一年份不同月份的数据会被合并。 - **与Sort Comparator区别**: - Sort Comparator决定数据全局顺序。 - Grouping Comparator决定Reduce方法中`key`的分组逻辑。 --- ### 关键区别总结 | **排序函数** | **作用阶段** | **核心目的** | **配置方法** | |-----------------------|--------------------|---------------------------------------|----------------------------------| | 默认键排序 | Shuffle | 自然顺序排列键 | 自动生效(键需实现`WritableComparable`) | | 自定义Sort Comparator | Shuffle | 覆盖默认排序规则 | `setSortComparatorClass()` | | RawComparator | Shuffle | 高效字节级比较 | 实现`RawComparator`接口 | | Grouping Comparator | Reduce分组 | 定义Reduce输入的分组逻辑 | `setGroupingComparatorClass()` | --- ### 应用场景示例 - **二次排序**: 需同时自定义`Sort Comparator`(控制整体顺序)和`Grouping Comparator`(控制分组逻辑)。 - **性能优化**: 使用`RawComparator`减少序列化/反序列化开销,适用于海量数据场景。 通过合理选择排序函数,可精准控制MapReduce任务的数据流与计算逻辑,提升处理效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值