13

设想:

对于具有 3 个元素的列表:

[A, B, C]

您可以根据需要多次循环访问它。

并且还有一个额外的计数功能记录每个元素的访问计数。

例如,如果访问它 7 次,应该返回:

[A, B, C, A, B, C, A]

并且每个元素的访问计数如下:

+–––––––––––+–––––––––––––––+
|  Element  |  Access count |
+–––––––––––––––––––––––––––+
|     A     |       3       |
+–––––––––––––––––––––––––––+
|     B     |       2       |
+–––––––––––––––––––––––––––+
|     C     |       2       |
+–––––––––––+–––––––––––––––+

添加另一个附加函数,允许调用者指定应过滤的元素列表。还是以 7 次访问为例,过滤[C]

[A, B, A, B, A, B, A]
+–––––––––––+–––––––––––––––+
|  Element  |  Access count |
+–––––––––––––––––––––––––––+
|     A     |       4       |
+–––––––––––––––––––––––––––+
|     B     |       3       |
+–––––––––––––––––––––––––––+
|     C     |       0       |
+–––––––––––+–––––––––––––––+

并且随后的调用getNextOne()应始终获取访问计数较低的那个。模拟负载平衡的访问计数实现。因此,如果第二个调用者尝试访问它 10 次,应该返回:

[C, C, C, B, C, A, B, C, A, B, C, A]
+–––––––––––+–––––––––––––––+
|  Element  |  Access count |
+–––––––––––––––––––––––––––+
|     A     |       7       |
+–––––––––––––––––––––––––––+
|     B     |       6       |
+–––––––––––––––––––––––––––+
|     C     |       6       |
+–––––––––––+–––––––––––––––+
4

3 回答 3

27

Guava 提供了一个Iterables.cycle(),加上一个Multiset用于计数,你就完成了:

package com.stackoverflow.so22869350;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multiset;

import java.util.Iterator;
import java.util.List;

public class Circular<T> {
    private final Multiset<T> counter;
    private final Iterator<T> elements;

    public Circular(final List<T> elements) {
        this.counter = HashMultiset.create();
        this.elements = Iterables.cycle(elements).iterator();
    }

    public T getOne() {
        final T element = this.elements.next();
        this.counter.add(element);
        return element;
    }

    public int getCount(final T element) {
        return this.counter.count(element);
    }

    public static void main(final String[] args) {
        final Circular<String> circular =
                new Circular<>(Lists.newArrayList("A", "B", "C"));
        for (int i = 0; i < 7; i++) {
            System.out.println(circular.getOne());
        }
        System.out.println("Count for A: " + circular.getCount("A"));
    }
}

输出:

A
B
C
A
B
C
A
Count for A: 3

注意:请注意使用适当的equals/hashCode类型T

于 2014-04-04T17:48:03.270 回答
7

上面的答案是正确的,只是在列表中添加了一种更简单的循环算法方法。

import java.util.Iterator;
import java.util.List;

public class MyRoundRobin<T> implements Iterable<T> {
    private List<T> coll;
    private int index = 0;

    public MyRoundRobin(List<T> coll) {
        this.coll = coll;
    }

    public Iterator<T> iterator() {
        return new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return true;
            }
            @Override
            public T next() {
                if (index >= coll.size()) {
                    index = 0;
                }
                T res = coll.get(index++);
                return res;
            }
            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
}
于 2019-04-10T09:24:56.620 回答
2

您可以基于单个TreeMap.

树状图
钥匙 Integer访问请求数
价值 List<T>请求访问的对象列表

班级CircularList<T>

方法 此代码可在 Java 7 中使用,无需额外的库
得到一个()
返回T访问请求数最少的第一个元素
getOne(List<T> 过滤器)
返回T过滤器中未包含的访问请求数最少的第一个元素
getOne(T 过滤器输入)
返回T过滤后的元素
getCount(T 元素)
返回int搜索元素的访问请求数,或者-1如果没有这样的元素
地位()
返回String地图的当前状态

在线尝试!

public class CircularList<T> {
    private final TreeMap<Integer, List<T>> elements = new TreeMap<>();

    /**
     * @param list required.
     */
    public CircularList(List<T> list) {
        if (list == null || list.size() == 0) return;
        this.elements.put(0, new ArrayList<>(list));
    }

    /**
     * @return the first element with the least number of access requests.
     */
    public synchronized T getOne() {
        // pull out the entry with the least number of access requests
        Map.Entry<Integer, List<T>> entry = this.elements.pollFirstEntry();
        Integer key = entry.getKey();
        List<T> value = entry.getValue();
        // pull out the first element from the list
        T element = value.remove(0);
        // if there is something left in the list, then put it back
        if (value.size() > 0) this.elements.put(key, value);
        // take the next list with greater number of access requests
        List<T> newValue = this.elements.get(key + 1);
        // create it if it doesn't exist
        if (newValue == null) newValue = new ArrayList<>();
        // add the current element to this list
        newValue.add(element);
        // update the map
        this.elements.put(key + 1, newValue);
        // return the first element with the least number of access requests
        return element;
    }

    /**
     * @param filter elements list that should be filtered.
     * @return the first element with the least number of
     * access requests that is not contained in the filter.
     */
    public synchronized T getOne(List<T> filter) {
        // incorrect filter is not applied
        if (filter == null || filter.size() == 0) return getOne();
        Integer key = -1;
        List<T> value;
        T element = null;
        // iterate over the entries of the map
        for (Map.Entry<Integer, List<T>> entry : this.elements.entrySet()) {
            key = entry.getKey();
            value = entry.getValue();
            element = null;
            // iterate over the elements of the list
            for (T el : value) {
                // the first element not contained in the filter
                if (!filter.contains(el)) {
                    element = el;
                    // remove this element from the list
                    value.remove(el);
                    // if there is nothing left in the list, remove the entry
                    if (value.size() == 0) this.elements.remove(key);
                    break;
                }
            }
            // if the element is found
            if (element != null) break;
        }
        // if no element is found, no filter is applied
        if (element == null) return getOne();
        // take the next list with greater number of access requests
        List<T> newValue = this.elements.get(key + 1);
        // create it if it doesn't exist
        if (newValue == null) newValue = new ArrayList<>();
        // add the current element to this list
        newValue.add(element);
        // update the map
        this.elements.put(key + 1, newValue);
        // return the first element with the least number of access requests
        return element;
    }

    /**
     * @param filterIn element that should be filtered.
     * @return the filtered element.
     */
    public synchronized T getOne(T filterIn) {
        // incorrect filter is not applied
        if (filterIn == null) return getOne();
        // iterate over the entries of the map
        for (Map.Entry<Integer, List<T>> entry : this.elements.entrySet()) {
            Integer key = entry.getKey();
            List<T> value = entry.getValue();
            // iterate over the elements of the list
            for (T element : value) {
                // if element is found
                if (filterIn.equals(element)) {
                    // remove this element from the list
                    value.remove(element);
                    // if there is nothing left in the list, remove the entry
                    if (value.size() == 0) this.elements.remove(key);
                    // take the next list with greater number of access requests
                    List<T> newValue = this.elements.get(key + 1);
                    // create it if it doesn't exist
                    if (newValue == null) newValue = new ArrayList<>();
                    // add the current element to this list
                    newValue.add(element);
                    // update the map
                    this.elements.put(key + 1, newValue);
                    // return filtered element
                    return element;
                }
            }
        }
        // if no element is found, no filter is applied
        return getOne();
    }

    /**
     * Search for the element in the lists of the map.
     *
     * @param element search element.
     * @return the number of access requests of the
     * search element, or -1 if there is no such element.
     */
    public int getCount(T element) {
        for (Map.Entry<Integer, List<T>> entry : this.elements.entrySet()) {
            if (entry.getValue().contains(element)) {
                return entry.getKey();
            }
        }
        return -1;
    }

    /**
     * @return the current status of the map.
     */
    public String status() {
        return elements.toString();
    }

    @Override
    public String toString() {
        return elements.toString();
    }
}
// Test
public static void main(String[] args) {
    CircularList<String> list =
            new CircularList<>(Arrays.asList("A", "B", "C", "D"));
    System.out.println(list); // {0=[A, B, C, D]}
    for (int i = 0; i < 10; i++) {
        System.out.print(list.getOne(Arrays.asList("A")) + " ");
        // B C D B C D B C D B
    }
    System.out.println();
    System.out.println(list.status()); // {0=[A], 3=[C, D], 4=[B]}
    for (int i = 0; i < 3; i++) {
        System.out.print(list.getOne("D") + " ");
        // D D D
    }
    System.out.println();
    System.out.println(list.status()); // {0=[A], 3=[C], 4=[B], 6=[D]}
    for (int i = 0; i < 14; i++) {
        System.out.print(list.getOne() + " ");
        // A A A C A B C A B C A D B C
    }
    System.out.println();
    System.out.println(list.status()); // {6=[A], 7=[D, B, C]}
    System.out.println(list.getCount("A")); // 6
    System.out.println(list.getCount("E")); // -1
}
于 2021-04-09T18:33:06.653 回答