All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.middleware.ushura.Chooser Maven / Gradle / Ivy

package com.alibaba.middleware.ushura;

import com.alibaba.middleware.ushura.poller.GenericPoller;
import com.alibaba.middleware.ushura.poller.Poller;
import com.alibaba.middleware.ushura.poller.PowerOfTwoPoller;
import com.alibaba.middleware.ushura.util.ThreadLocalRandom;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Chooser {

    private K uniqueKey;

    private volatile Ref ref;

    public T random() {
        List items = ref.items;
        if (items.size() == 0)
            return null;
        if (items.size() == 1)
            return items.get(0);
        return items.get(ThreadLocalRandom.current().nextInt(items.size()));
    }

    public T randomWithWeight() {
        if(ref.items.size() == 0)
            return null;
        Ref ref = this.ref;
        double random = ThreadLocalRandom.current().nextDouble(0, 1);
        int index = Arrays.binarySearch(ref.weights, random);
        if (index < 0) {
            index = -index - 1;
        } else {
            return ref.items.get(index);
        }

        if (index >= 0 && index < ref.weights.length) {
            if (random < ref.weights[index]) {
                return ref.items.get(index);
            }
        }

        /* This should never happen, but it ensures we will return a correct
         * object in case there is some floating point inequality problem
         * wrt the cumulative probabilities. */
        return ref.items.get(ref.items.size() - 1);
    }

    public T poll() {
        if (ref.poller == null)
            throw new IllegalStateException("You cannot call this method before you set a poller to a chooser.");
        return ref.poller.next();
    }

    public Chooser(K uniqueKey) {
        this(uniqueKey, new ArrayList>());
    }

    public Chooser(K uniqueKey, List> pairs) {
        Ref ref = new Ref(pairs);
        ref.refresh();
        this.uniqueKey = uniqueKey;
        this.ref = ref;
    }

    public Chooser poller(Poller.PollerType type) {
        switch (type) {
            case Generic: {
                ref.poller = new GenericPoller(ref.items);
                break;
            }
            case PowerOfTwoPoller: {
                ref.poller = new PowerOfTwoPoller(ref.items);
            }
            default: {
                ref.poller = new GenericPoller(ref.items);
            }
        }
        return this;
    }

    public K getUniqueKey() {
        return uniqueKey;
    }

    public Ref getRef() {
        return ref;
    }

    public void refresh(List> itemsWithWeight) {
        Ref newRef = new Ref(itemsWithWeight);
        newRef.refresh();
        newRef.poller = this.ref.poller.refresh(newRef.items);
        this.ref = newRef;
    }

    public class Ref {
        private List> itemsWithWeight = new ArrayList>();
        private List items = new ArrayList();
        private Poller poller = new GenericPoller(items);
        private double[] weights;

        @SuppressWarnings("unchecked")
        public Ref(List> itemsWithWeight) {
            this.itemsWithWeight = itemsWithWeight;
        }

        public void refresh() {
            Double originWeightSum = (double) 0;

            for (Pair item : itemsWithWeight) {

                double weight = item.weight();
                if (!(weight > 0)) //ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest
                    continue;

                items.add(item.item());
                if (Double.isInfinite(weight)) {
                    weight = 10000.0D;
                }
                if (Double.isNaN(weight)) {
                    weight = 1.0D;
                }
                originWeightSum += weight;
            }

            double[] exactWeights = new double[items.size()];
            int index = 0;
            for (Pair item : itemsWithWeight) {
                double singleWeight = item.weight();
                if(!(singleWeight > 0)) continue; //ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest
                exactWeights[index++] = singleWeight / originWeightSum;
            }

            weights = new double[items.size()];
            double randomRange = 0D;
            for (int i = 0; i < index; i++) {
                weights[i] = randomRange + exactWeights[i];
                randomRange += exactWeights[i];
            }

            if (index != 0 && !(Math.abs(weights[index - 1] - Double.valueOf(1)) < 0.0001)) {
                throw new IllegalStateException("Cumulative Weight caculate wrong , the sum of probabilities does not equals 1.");
            }
        }

        @Override
        public int hashCode() {
            return itemsWithWeight.hashCode();
        }

        @SuppressWarnings("unchecked")
        @Override
        public boolean equals(Object other) {
            if (this == other)
                return true;
            if (other == null)
                return false;
            if (getClass() != other.getClass())
                return false;
            if (!(other.getClass().getGenericInterfaces()[0].equals(this.getClass().getGenericInterfaces()[0])))
                return false;
            Ref otherRef = (Ref) other;
            if (itemsWithWeight == null) {
                if (otherRef.itemsWithWeight != null)
                    return false;
            } else {
                if (otherRef.itemsWithWeight == null)
                    return false;
                else
                    return this.itemsWithWeight.equals(otherRef.itemsWithWeight);
            }
            return true;
        }

        public double[] weights() {
            return weights;
        }

        public boolean contains(T item) {
            return items.contains(item);
        }
    }

    @Override
    public int hashCode() {
        return uniqueKey.hashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (this == other)
            return true;
        if (other == null)
            return false;
        if (getClass() != other.getClass())
            return false;

        Chooser otherChooser = (Chooser) other;
        if (this.uniqueKey == null) {
            if (otherChooser.getUniqueKey() != null)
                return false;
        } else {
            if (otherChooser.getUniqueKey() == null)
                return false;
            else {
                if (!this.uniqueKey.equals(otherChooser.getUniqueKey()))
                    return false;
            }
        }

        if (this.ref == null) {
            if (otherChooser.getRef() != null)
                return false;
        } else {
            if (otherChooser.getRef() == null)
                return false;
            else {
                if (!this.ref.equals(otherChooser.getRef()))
                    return false;
            }
        }

        return true;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy