I am trying to make general class to find mode. I am looking for some general feedback on how I can improve the structure and efficiency of my code.

```
package analysis.statistic;

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Mode {

/**
 * Return objects which appears most often.
 * 
 * @return Map<object,countAppears> most appears objects.
 */
@SuppressWarnings("unchecked")
public static <T> Map<T, Integer> mode(T... objects) {
    Objects.requireNonNull(objects, "objects must not be null");
    if (objects.length == 0) {
        return new HashMap<>();
    }
    Arrays.sort(objects);
    ModeCalc<T> calc = new ModeCalc<T>(objects[0]);
    for (T t : objects) {
        calc.checkMaxAppears(t);
    }

    return calc.getMode();

}

/**
 * Work like {@link #mode(Object...)}.
 */
@SuppressWarnings("unchecked")
public static <T> Map<T, Integer> mode(Comparator<? super T> c, T... objects) {
    Objects.requireNonNull(objects, "objects must not be null");
    if (objects.length == 0) {
        return new HashMap<>();
    }
    Arrays.sort(objects, c);
    ModeCalc<T> calc = new ModeCalc<T>(objects[0]);
    for (T t : objects) {
        calc.checkMaxAppears(t);

    }

    return calc.getMode();

}

/**
 * Work like {@link #mode(Object...)}.
 */
public static Map<Integer, Integer> mode(int... numbers) {
    Objects.requireNonNull(numbers, "numbers must not be null");
    if (numbers.length == 0) {
        return new HashMap<>();
    }
    Arrays.sort(numbers);
    ModeCalc<Integer> calc = new ModeCalc<Integer>(numbers[0]);
    for (int t : numbers) {
        calc.checkMaxAppears(t);
    }

    return calc.getMode();
}

/**
 * Work like {@link #mode(Object...)}.
 */
public static Map<Long, Integer> mode(long... numbers) {
    Objects.requireNonNull(numbers, "numbers must not be null");
    if (numbers.length == 0) {
        return new HashMap<>();
    }
    Arrays.sort(numbers);
    ModeCalc<Long> calc = new ModeCalc<>(numbers[0]);
    for (long t : numbers) {
        calc.checkMaxAppears(t);

    }

    return calc.getMode();
}

/**
 * Work like {@link #mode(Object...)}.
 */
public static Map<Double, Integer> mode(double... numbers) {
    Objects.requireNonNull(numbers, "numbers must not be null");
    if (numbers.length == 0) {
        return new HashMap<>();
    }
    Arrays.sort(numbers);
    ModeCalc<Double> calc = new ModeCalc<Double>(numbers[0]);
    for (double t : numbers) {
        calc.checkMaxAppears(t);
    }
    return calc.getMode();
}

/**
 * Work like {@link #mode(Object...)}.
 */
public static Map<Float, Integer> mode(float... numbers) {
    Objects.requireNonNull(numbers, "numbers must not be null");
    if (numbers.length == 0) {
        return new HashMap<>();
    }

    Arrays.sort(numbers);
    ModeCalc<Float> modeCalc = new ModeCalc<Float>(numbers[0]);
    for (float t : numbers) {
        modeCalc.checkMaxAppears(t);
    }
    return modeCalc.getMode();
}

/**
 * Work like {@link #mode(Object...)}.
 */
public static Map<String, Integer> mode(String... strings) {
    Objects.requireNonNull(strings, "strings must not be null");
    if (strings.length == 0) {
        return new HashMap<>();
    }
    Arrays.sort(strings);
    ModeCalc<String> state = new ModeCalc<>(strings[0]);
    for (String t : strings) {
        state.checkMaxAppears(t);
    }
    return state.getMode();
}

private static class ModeCalc<T> {
    private int nTimesLastObjectAppears = 0;
    private int maxTimeObjectAppears = 0;
    private T prevObject;
    Map<T, Integer> mostAppearsObjects;

    public ModeCalc(T firstObjectInArray) {
        prevObject = firstObjectInArray;
        mostAppearsObjects = new HashMap<>();
    }

    void checkMaxAppears(T currentObject) {
        if (currentObject.equals(prevObject)) {
            nTimesLastObjectAppears += 1;
        } else {
            addObjectToMap();
            prevObject = currentObject;
            nTimesLastObjectAppears = 1;
        }
    }

    void addObjectToMap() {
        if (nTimesLastObjectAppears > maxTimeObjectAppears) {
            mostAppearsObjects.clear();
            mostAppearsObjects.put(prevObject, nTimesLastObjectAppears);
            maxTimeObjectAppears = nTimesLastObjectAppears;
        } else if (nTimesLastObjectAppears == maxTimeObjectAppears) {
            mostAppearsObjects.put(prevObject, nTimesLastObjectAppears);
        }
    }

    Map<T, Integer> getMode() {
        // to check appears of last object of loop and add it to map
        addObjectToMap();
        return mostAppearsObjects;
    }
}

}

```