@SuppressWarnings("unchecked")
    public AliasedMethodTable(Map<E, Double> elementsToWeight) {
        if (requireNonNull(elementsToWeight, "elementsToWeight is null").size() == 0) {
            throw new IllegalArgumentException("elementsToWeight is empty");
        }
        elements = (E[]) elementsToWeight.keySet().toArray();// nulls allowed
        probabilities = new double[elements.length];
        alias = new int[elements.length];
        weights = new double[elements.length];
        double averageProbability = 1.0d / elements.length;

        // Calculates the weighted sum.
        Double[] p = elementsToWeight.values().toArray(new Double[elements.length]);
        double weightedSum = 0d;
        for (int i = 0; i < p.length; i++) {
            weightedSum += this.weights[i] = p[i];// also checks for null
        }

        // Some temporary work lists.
        ArrayDeque<Integer> smallest = new ArrayDeque<>();
        ArrayDeque<Integer> largest = new ArrayDeque<>();

        // Normalize weights and if probability is below the average probability add to small, otherwise add to big
        for (int i = 0; i < p.length; i++) {
            this.weights[i] = p[i];
            ((p[i] /= weightedSum) >= averageProbability ? largest : smallest).add(i);

        }
        // Do to floating point inaccuracies we might exhaust largest list first so check both
        while (!smallest.isEmpty() && !largest.isEmpty()) {
            int iSmall = smallest.removeLast();
            int iLarge = alias[iSmall] = largest.removeLast();
            probabilities[iSmall] = p[iSmall] * p.length;
            // Decrease the probability of the larger one by the appropriate amount.
            // If the new probability is less than the average, add it into the small list; otherwise add it to the
            // large list.
            ((p[iLarge] += p[iSmall] - averageProbability) >= 1.0d / p.length ? largest : smallest).add(iLarge);
        }
        smallest.addAll(largest); // Remaining probabilities are all 1/n.
        while (!smallest.isEmpty()) {
            probabilities[smallest.removeLast()] = 1.0d;
        }
    }