Java-итераторы
У меня есть список хостов в массиве, который представляет серверы, доступные для выполнения определенной работы. В настоящее время я просто перебираю список, просматривая и устанавливаю связь с хостом, чтобы проверить, что он не занят. Если нет, я отправлю ему задание. Этот подход имеет тенденцию означать, что первый хост в списке имеет тенденцию становиться горячим, так как загрузка не сбалансирована должным образом с остальными доступными хостами.
в псевдокоде..
for (Host h : hosts) {
//checkstatus
if status == job accepted break;
}
Я хотел бы сбалансировать эту нагрузку должным образом между хостами, а первый хост один используется 2-й раз, когда используется метод host 2. Просто интересно, что наиболее элегантным решением для этого является
Спасибо
Вт
Ответы
Ответ 1
Вы можете создать новый тип Iterable, который обеспечивает циклическую итерацию:
public class RoundRobin<T> implements Iterable<T> {
private List<T> coll;
public RoundRobin(List<T> coll) { this.coll = coll; }
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
T res = coll.get(index);
index = (index + 1) % coll.size();
return res;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
Вам нужно определить ваши хосты как RoundRobin<Host>
.
[ФИКСИРОВАН на основе комментария Мирко]
Ответ 2
Коллекции Google имеет метод утилиты Iterators.cycle(Iterable<T> iterable)
это делает то, что вы хотите.
Ответ 3
Если список изменен и стоимость его редактирования незначительна по сравнению с вводом-выводом с хостами, вы можете просто повернуть его:
List<String> list = Arrays.asList("one", "two", "three");
Collections.rotate(list, -1);
System.out.println(list);
Ответ 4
IMHO стандартный Java API уже предоставляет простой способ достичь этого, не прибегая к использованию внешних библиотек или даже необходимости внедрять пользовательский Iterator. Просто используйте Deque, где вы вытаскиваете первый сервер, используете или отбрасываете его, а затем добавляете его обратно в конец Deque. Вот пример кода:
// Initialize the Deque. This might be at your class constructor.
Deque<Host> dq = new ArrayDeque<Host>();
dq.addAll(Arrays.asList(hosts));
void sendJob(Job myJob) {
boolean jobInProcess = false;
do {
Host host = dq.removeFirst(); // Remove the host from the top
if(!host.isBusy()) {
host.sendJob(myJob);
jobInProcess = true;
}
dq.addLast(host); // Put the host back at the end
}
while(!jobInProcess); // Might add another condition to prevent an infinite loop...
}
Это всего лишь образец, в котором вы всегда выполняете ping-хосты в циклическом порядке в цикле, который заканчивается только тогда, когда один из них доступен и выполняет задание. Вы могли легко перемещаться с ним, чтобы идти только вокруг очереди один раз (использовать счетчик с максимальным значением в размере очереди) или несколько раз исключать исключение или спать между раундами, чтобы избежать ударов хостов, когда все заняты,
Ответ 5
Моя реализация RoundRobin, основанная на реализации fooobar.com/questions/510967/...
/**
*
* @author Mirko Schulze
*
* @param <T>
*/
public class RoundRobin<T> implements Iterable<T> {
private final List<T> coll;
public RoundRobin(final List<T> coll) {
this.coll = NullCheck.throwExceptionIfNull(coll, "collection is null");
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
this.index = this.index % RoundRobin.this.coll.size();
final T t = RoundRobin.this.coll.get(this.index);
this.index++;
return t;
}
@Override
public void remove() {
throw new IllegalArgumentException("remove not allowd");
}
};
}
}
И Junit TestCase
/**
*
* @author Mirko Schulze
*
*/
@RunWith(JUnit4.class)
public class RoundRobinTest extends TestCase {
private List<Integer> getCollection() {
final List<Integer> retval = new Vector<Integer>();
retval.add(Integer.valueOf(1));
retval.add(Integer.valueOf(2));
retval.add(Integer.valueOf(3));
retval.add(Integer.valueOf(4));
retval.add(Integer.valueOf(5));
return retval;
}
@Test
public void testIteration() {
final List<Integer> l = this.getCollection();
final Integer frst = l.get(0);
final Integer scnd = l.get(1);
final Integer thrd = l.get(2);
final Integer frth = l.get(3);
final Integer last = l.get(4);
Assert.assertEquals("die Collection hat für diesen Test nicht die passende Größe!", 5, l.size());
final RoundRobin<Integer> rr = new RoundRobin<Integer>(l);
final Iterator<Integer> i = rr.iterator();
for (int collectionIterations = 0; collectionIterations < 4; collectionIterations++) {
final Integer i1 = i.next();
Assert.assertEquals("nicht das erste Element", frst, i1);
final Integer i2 = i.next();
Assert.assertEquals("nicht das zweite Element", scnd, i2);
final Integer i3 = i.next();
Assert.assertEquals("nicht das dritte Element", thrd, i3);
final Integer i4 = i.next();
Assert.assertEquals("nicht das vierte Element", frth, i4);
final Integer i5 = i.next();
Assert.assertEquals("nicht das letzte Element", last, i5);
}
}
}
Ответ 6
Если вы создаете Итератор, лучше сначала создать защитную копию и обработать итератор.
return new MyIterator(ImmutableList.<T>copyOf(list));
Ответ 7
public class RoundRobinIterator<T> implements Serializable {
private static final long serialVersionUID = -2472203060894189676L;
//
private List<T> list;
private Iterator<T> it;
private AtomicInteger index = new AtomicInteger(0);
public RoundRobinIterator(List<T> list) throws NullPointerException {
super();
if (list==null) {
throw new NullPointerException("List is null");
}
this.list=Collections.unmodifiableList(list);
}
public RoundRobinIterator(Collection<T> values) {
this(new ArrayList<T>(values));
}
public RoundRobinIterator(Iterator<T> values) {
this(copyIterator(values));
}
public RoundRobinIterator(Enumeration<T> values) {
this(Collections.list(values));
}
private final List<T> getList() {
return list;
}
private final Iterator<T> getIt() {
return it;
}
public final int size() {
return list.size();
}
public final synchronized T getNext(Filter<T> filter) {
int start = index.get();
T t = getNext();
T result = null;
while ((result==null) && (start!=getIndex())) {
if (filter.accept(t)) {
result=t;
} else {
t = getNext();
}
}
return result;
}
public final synchronized T getNext() {
if (getIt()==null) {
if (getList().size()==0) {
index.set(0);
return null;
} else {
it = getList().iterator();
index.set(0);
return it.next();
}
} else if (it.hasNext()) {
index.incrementAndGet();
return it.next();
} else {
if (list.size()==0) {
index.set(0);
return null;
} else {
index.set(0);
it = list.iterator();
return it.next();
}
}
}
public final synchronized int getIndex() {
return index.get();
}
private static <T> List<T> copyIterator(Iterator<T> iter) {
List<T> copy = new ArrayList<T>();
while (iter.hasNext()) {
copy.add(iter.next());
}
return copy;
}
}
Где фильтр
public interface Filter<T> {
public boolean accept(T t);
}