A more elegant way to deal with timers without using delay()?
For excact timing there is feature called ADC Auto Trigger mode. For example millis() are usually counted by timer 0 overflow (on AVR based Arduinos) every 1024us (I suppose it's because of PWM - otherwise it could be set exactly to 1ms). So if you don't mind that little offset, you can configure 
ADC with Timer 0 Overflow and use every 20 measure only.
But don't forget that analogRead on another pin changes ADC channel and you will get something else if you don't check it.

As an auto trigger source can be used one of these:


Free Running Mode
Analog Comparator
External Interrupt 0
Timer/Counter 0 Compare Match A
Timer/Counter 0 Overflow
Timer/Counter 1 Compare Match B
Timer/Counter 1 Overflow
Timer/Counter 1 Capture Event


For ATMega328 it can be something like this:

void setup() {
    Serial.begin(57600);

    // ADC setup is done by arduino framework, but it's possible to change it slightly (for ATMega328) :
    ADCSRB  = _BV(ADTS2); // ADTS2..0 = 100, Timer 0 overflow as trigger source
    ADCSRA |= _BV(ADATE); // enable auto trigger mode    
    ADCSRA |= _BV(ADIF); // reset conversion interrupt flag (by writing log 1)

    analogRead(A0); // dummy read to set correct channel and to start auto trigger mode
}

int8_t sample_count = 0;
int8_t sample_index = 0;
uint16_t  samples[] = {0, 0, 0, 0, 0};
uint16_t sample_sum = 0;

void loop() {
    if (ADCSRA &amp; _BV(ADIF)) {
        ADCSRA |= _BV(ADIF); // reset ADIF flag by writing logic 1 into it (mentioned in datasheet)
        if (++sample_count == 20) {
            sample_count = 0; // reset sample_count, as we wan't every 20th

            sample_sum -= samples[sample_index];
            samples[sample_index] = ADC; // read value
            sample_sum += samples[sample_index];

            if (++sample_index == 5) { // reset index and count and send average
                sample_index = 0;
                Serial.println(sample_sum);
            }
        }
    }
}