Сценарий интервью "Последние 100 байт"
Я получил этот вопрос в интервью на днях и хотел бы узнать несколько наилучших ответов (я не очень хорошо ответил ха-ха):
Сценарий: существует веб-страница, которая отслеживает байты, отправленные по какой-либо сети. Каждый раз, когда отправляется байт, функция recordByte() называется передачей этого байта, это может происходить сто тысяч раз в день. На этой странице есть кнопка, которая при нажатии отображает последние 100 байт, переданных на экран recordByte() на экране (это происходит, вызывая метод печати ниже).
Следующий код - это то, что мне было дано, и его просили заполнить:
public class networkTraffic {
public void recordByte(Byte b){
}
public String print() {
}
}
Каков наилучший способ хранения 100 байтов? Список? Любопытно, как это лучше всего сделать.
Ответы
Ответ 1
Что-то вроде этого (круговой буфер):
byte[] buffer = new byte[100];
int index = 0;
public void recordByte(Byte b) {
index = (index + 1) % 100;
buffer[index] = b;
}
public void print() {
for(int i = index; i < index + 100; i++) {
System.out.print(buffer[i % 100]);
}
}
Преимущества использования кругового буфера:
- Вы можете зарезервировать пространство статически. В сетевом приложении реального времени (VoIP, потоковая передача,..) это часто делается потому, что вам не нужно хранить все данные передачи, а только окно, содержащее новые байты для обработки.
- Это быстро: может быть реализовано с массивом со стоимостью чтения и записи O (1).
Ответ 2
Я не знаю java, но должна существовать концепция очереди, в которой вы должны вставлять байты до тех пор, пока количество элементов в очереди не достигнет 100, после чего вы удалите один байт, а затем запустите другой.
public void recordByte(Byte b)
{
if (queue.ItemCount >= 100)
{
queue.dequeue();
}
queue.enqueue(b);
}
Вы можете печатать, просматривая пункты:
public String print()
{
foreach (Byte b in queue)
{
print("X", b); // some hexadecimal print function
}
}
Ответ 3
Круговой буфер с использованием массива:
- Массив из 100 байт
- Следите за тем, где индекс головы i
- Для
recordByte()
поместите текущий байт в [i] и я = я + 1% 100;
- Для
print()
возвратный подмассиво (i + 1, 100) объединяется с подмассивом (0, i)
Очередь с помощью связанного списка (или очереди Java):
- Для
recordByte()
добавить новый байт в конец
- Если новая длина будет больше 100, удалите первый элемент
- Для
print()
просто распечатайте список
Ответ 4
Вот мой код. Это может показаться немного неясным, но я уверен, что это самый быстрый способ сделать это (по крайней мере, это было бы на С++, не так уверенном в Java):
public class networkTraffic {
public networkTraffic() {
_ary = new byte[100];
_idx = _ary.length;
}
public void recordByte(Byte b){
_ary[--_idx] = b;
if (_idx == 0) {
_idx = _ary.length;
}
}
private int _idx;
private byte[] _ary;
}
Некоторые примечания:
- При вызове recordByte() не выделяются/освобождаются данные.
- Я не использовал%, потому что он медленнее прямого сравнения и использует if (возможно, здесь может помочь предсказание ветвления)
-
--_idx
быстрее, чем _idx--
, потому что не задействована временная переменная.
- Я возвращаю назад в 0, потому что тогда мне не нужно получать
_ary.length
каждый раз в вызове, но только каждые 100 раз, когда достигается первая запись. Возможно, это не обязательно, компилятор может позаботиться об этом.
- если было меньше 100 вызовов recordByte(), остальные - нули.
Ответ 5
Самое простое - засунуть его в массив. Максимальный размер, который может разместить массив, составляет 100 байт. Продолжайте добавлять байты, когда они выходят из Интернета. После того, как первые 100 байтов находятся в массиве, когда приходит 101-й байт, удалите байт в голове (т.е. 0-й). Продолжайте делать это. Это в основном очередь. Концепция FIFO. Если загрузка завершена, вы остаетесь с последними 100 байтами.
Не сразу после загрузки, но в любой момент времени этот массив будет иметь последние 100 байт.
@Yottagray Не получается, где проблема? Кажется, что существует целый ряд общих подходов (массив, круговой массив и т.д.) И ряд специфических для языка подходов (byteArray и т.д.). Я что-то пропустил?
Ответ 6
Многопоточное решение с неблокируемым вводом-выводом:
private static final int N = 100;
private volatile byte[] buffer1 = new byte[N];
private volatile byte[] buffer2 = new byte[N];
private volatile int index = -1;
private volatile int tag;
synchronized public void recordByte(byte b) {
index++;
if (index == N * 2) {
//both buffers are full
buffer1 = buffer2;
buffer2 = new byte[N];
index = N;
}
if (index < N) {
buffer1[index] = b;
} else {
buffer2[index - N] = b;
}
}
public void print() {
byte[] localBuffer1, localBuffer2;
int localIndex, localTag;
synchronized (this) {
localBuffer1 = buffer1;
localBuffer2 = buffer2;
localIndex = index;
localTag = tag++;
}
int buffer1Start = localIndex - N >= 0 ? localIndex - N + 1 : 0;
int buffer1End = localIndex < N ? localIndex : N - 1;
printSlice(localBuffer1, buffer1Start, buffer1End, localTag);
if (localIndex >= N) {
printSlice(localBuffer2, 0, localIndex - N, localTag);
}
}
private void printSlice(byte[] buffer, int start, int end, int tag) {
for(int i = start; i <= end; i++) {
System.out.println(tag + ": "+ buffer[i]);
}
}
Ответ 7
Просто для этого. Как насчет использования ArrayList<Byte>
? Скажи почему нет?
public class networkTraffic {
static ArrayList<Byte> networkMonitor; // ArrayList<Byte> reference
static { networkMonitor = new ArrayList<Byte>(100); } // Static Initialization Block
public void recordByte(Byte b){
networkMonitor.add(b);
while(networkMonitor.size() > 100){
networkMonitor.remove(0);
}
}
public void print() {
for (int i = 0; i < networkMonitor.size(); i++) {
System.out.println(networkMonitor.get(i));
}
// if(networkMonitor.size() < 100){
// for(int i = networkMonitor.size(); i < 100; i++){
// System.out.println("Emtpy byte");
// }
// }
}
}