public static NamedFieldsMapping mapping( String[] actualFieldNames, String[] metaFieldNames ) {
    LinkedHashMap<String, List<Integer>> metaNameToIndex = new LinkedHashMap<>();
    List<Integer> unmatchedMetaFields = new ArrayList<>();
    int[] actualToMetaFieldMapping = new int[ actualFieldNames == null ? 0 : actualFieldNames.length];

    for ( int i = 0; i < metaFieldNames.length; i++ ) {
      List<Integer> coll = metaNameToIndex.getOrDefault( metaFieldNames[i], new ArrayList<>() );
      coll.add( i );
      metaNameToIndex.put( metaFieldNames[i], coll );
    }

    if ( actualFieldNames != null ) {
      for ( int i = 0; i < actualFieldNames.length; i++ ) {
        List<Integer> columnIndexes = metaNameToIndex.get( actualFieldNames[ i ] );
        if ( columnIndexes == null || columnIndexes.isEmpty() ) {
          unmatchedMetaFields.add( i );
          actualToMetaFieldMapping[ i ] = FIELD_DOES_NOT_EXIST;
          continue;
        }
        actualToMetaFieldMapping[ i ] = columnIndexes.remove( 0 );
      }
    }

    Iterator<Integer> remainingMetaIndexes = metaNameToIndex.values().stream()
      .flatMap( List::stream )
      .sorted()
      .iterator();

    for ( int idx : unmatchedMetaFields ) {
      if ( !remainingMetaIndexes.hasNext() ) {
        break;
      }
      actualToMetaFieldMapping[ idx ] = remainingMetaIndexes.next();
    }

    return new NamedFieldsMapping( actualToMetaFieldMapping );
  }
@Test
  public void mapping() {
    NamedFieldsMapping mapping =
        NamedFieldsMapping.mapping( new String[] { "FIRST", "SECOND", "THIRD" }, new String[] { "SECOND", "THIRD" } );
    assertEquals( 0, mapping.fieldMetaIndex( 1 ) );
  }