private static Collection<Map.Entry<SJSourceTarget, List<ReduceSinkOperator>>> createMergeCandidates(
      Map<ReduceSinkOperator, SemiJoinBranchInfo> semijoins) {
    // Predictable iteration helps to avoid small changes in the plans
    LinkedHashMap<SJSourceTarget, List<ReduceSinkOperator>> sjGroups = new LinkedHashMap<>();
    for (Map.Entry<ReduceSinkOperator, SemiJoinBranchInfo> smjEntry : semijoins.entrySet()) {
      TableScanOperator ts = smjEntry.getValue().getTsOp();
      // Semijoin optimization branch should look like <Parent>-SEL-GB1-RS1-GB2-RS2
      SelectOperator selOp = OperatorUtils.ancestor(smjEntry.getKey(), SelectOperator.class, 0, 0, 0, 0);
      Operator<?> source = selOp.getParentOperators().get(0);
      checkState(selOp.getParentOperators().size() == 1, "Semijoin branches should not have multiple parents");
      SJSourceTarget sjKey = new SJSourceTarget(source, ts);
      List<ReduceSinkOperator> ops = sjGroups.computeIfAbsent(sjKey, tableScanOperator -> new ArrayList<>());
      ops.add(smjEntry.getKey());
    }
    return sjGroups.entrySet();
  }
Groups single column semijoin reducer branches together if they have the same source and target relation.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/SemiJoinReductionMerge.java