Efficiently insert or replace multiple elements in the middle or at the beginning of a Vec?

前端 未结 3 1925
滥情空心
滥情空心 2020-11-27 20:45

Is there any straightforward way to insert or replace multiple elements from &[T] and/or Vec in the middle or at the beginning of a

相关标签:
3条回答
  • 2020-11-27 21:16

    I was trying to prepend to a vector in rust and found this closed question that was linked here, (despite this question being both prepend and insert AND efficiency. I think my answer would be better as an answer for that other, more precises question because I can't attest to the efficiency), but the following code helped me prepend, (and the opposite.) [I'm sure that the other two answers are more efficient, but the way that I learn, I like having answers that can be cut-n-pasted with examples that demonstrate an application of the answer.]

    pub trait Unshift<T> { fn unshift(&mut self, s: &[T]) -> (); }
    pub trait UnshiftVec<T> { fn unshift_vec(&mut self, s: Vec<T>) -> (); }
    pub trait UnshiftMemoryHog<T> { fn unshift_memory_hog(&mut self, s: Vec<T>) -> (); }
    pub trait Shift<T> { fn shift(&mut self) -> (); }
    pub trait ShiftN<T> { fn shift_n(&mut self, s: usize) -> (); }
    
    impl<T: std::clone::Clone> ShiftN<T> for Vec<T> {
        fn shift_n(&mut self, s: usize) -> ()
        // where
        //    T: std::clone::Clone,
        {   
            self.drain(0..s);
        }
    }
    
    impl<T: std::clone::Clone> Shift<T> for Vec<T> {
        fn shift(&mut self) -> ()
        // where
        //    T: std::clone::Clone,
        {   
            self.drain(0..1);
        }
    }
    
    impl<T: std::clone::Clone> Unshift<T> for Vec<T> {
        fn unshift(&mut self, s: &[T]) -> ()
        // where
        //    T: std::clone::Clone,
        {   
            self.splice(0..0, s.to_vec());
        }
    }
    impl<T: std::clone::Clone> UnshiftVec<T> for Vec<T> {
        fn unshift_vec(&mut self, s: Vec<T>) -> ()
        where
            T: std::clone::Clone,
        {   
            self.splice(0..0, s);
        }
    }
    
    impl<T: std::clone::Clone> UnshiftMemoryHog<T> for Vec<T> {
        fn unshift_memory_hog(&mut self, s: Vec<T>) -> ()
        where
            T: std::clone::Clone,
        {
            let mut tmp: Vec<_> = s.to_owned();
            //let mut tmp: Vec<_> = s.clone(); // this also works for some data types
            /*
            let local_s: Vec<_> = self.clone(); // explicit clone()
            tmp.extend(local_s);                // to vec is possible
            */
            tmp.extend(self.clone());
            *self = tmp;
            //*self = (*tmp).to_vec(); // Just because it compiles, doesn't make it right.
        }
    }
    
    // this works for: v = unshift(v, &vec![8]);
    // (If you don't want to impl Unshift for Vec<T>)
    
    #[allow(dead_code)]
    fn unshift_fn<T>(v: Vec<T>, s: &[T]) -> Vec<T>
    where
        T: Clone,
    {
        // create a mutable vec and fill it
        // with a clone of the array that we want
        // at the start of the vec.
        let mut tmp: Vec<_> = s.to_owned();
        // then we add the existing vector to the end
        // of the temporary vector.
        tmp.extend(v);
        // return the tmp vec that is identitcal
        // to unshift-ing the original vec.
        tmp
    }
    
    /*
        N.B. It is sometimes (often?) more memory efficient to reverse
        the vector and use push/pop, rather than splice/drain;
        Especially if you create your vectors in "stack order" to begin with.
    */
    
    fn main() {
        let mut v: Vec<usize> = vec![1, 2, 3];
        println!("Before push:\t {:?}", v);
        v.push(0);
        println!("After push:\t {:?}", v);
        v.pop();
        println!("popped:\t\t {:?}", v);
        v.drain(0..1);
        println!("drain(0..1)\t {:?}", v);
        /*
            // We could use a function
        let c = v.clone();
        v = unshift_fn(c, &vec![0]);
        */
        v.splice(0..0, vec![0]);
        println!("splice(0..0, vec![0]) {:?}", v);
        v.shift_n(1);
        println!("shift\t\t {:?}", v);
        v.unshift_memory_hog(vec![8, 16, 31, 1]);
        println!("MEMORY guzzler unshift {:?}", v);
        //v.drain(0..3);
        v.drain(0..=2);
        println!("back to the start: {:?}", v);
        v.unshift_vec(vec![0]);
        println!("zerothed with unshift: {:?}", v);
    
        let mut w = vec![4, 5, 6];
        /*
        let prepend_this = &[1, 2, 3];
        w.unshift_vec(prepend_this.to_vec());
        */
        w.unshift(&[1, 2, 3]);
    
        assert_eq!(&w, &[1, 2, 3, 4, 5, 6]);
        println!("{:?} == {:?}", &w, &[1, 2, 3, 4, 5, 6]);
    }
    
    0 讨论(0)
  • 2020-11-27 21:23

    Okay, there is no appropriate method in Vec interface (as I can see). But we can always implement the same thing ourselves.

    memmove

    When T is Copy, probably the most obvious way is to move the memory, like this:

    fn push_all_at<T>(v: &mut Vec<T>, offset: usize, s: &[T]) where T: Copy {
        match (v.len(), s.len()) {
            (_, 0) => (),
            (current_len, _) => {
                v.reserve_exact(s.len());
                unsafe {
                    v.set_len(current_len + s.len());
                    let to_move = current_len - offset;
                    let src = v.as_mut_ptr().offset(offset as isize);
                    if to_move > 0 {
                        let dst = src.offset(s.len() as isize);
                        std::ptr::copy_memory(dst, src, to_move);
                    }
                    std::ptr::copy_nonoverlapping_memory(src, s.as_ptr(), s.len());
                }
            },
        }
    }
    

    shuffle

    If T is not copy, but it implements Clone, we can append given slice to the end of the Vec, and move it to the required position using swaps in linear time:

    fn push_all_at<T>(v: &mut Vec<T>, mut offset: usize, s: &[T]) where T: Clone + Default {
        match (v.len(), s.len()) {
            (_, 0) => (),
            (0, _) => { v.push_all(s); },
            (_, _) => {
                assert!(offset <= v.len());
                let pad = s.len() - ((v.len() - offset) % s.len());
                v.extend(repeat(Default::default()).take(pad));
                v.push_all(s);
                let total = v.len();
                while total - offset >= s.len() {
                    for i in 0 .. s.len() { v.swap(offset + i, total - s.len() + i); }
                    offset += s.len();
                }
                v.truncate(total - pad);
            },
        }
    }
    

    iterators concat

    Maybe the best choice will be to not modify Vec at all. For example, if you are going to access the result via iterator, we can just build iterators chain from our chunks:

    let v: &[usize] = &[0, 1, 2];
    let s: &[usize] = &[3, 4, 5, 6];
    let offset = 2;
    let chain = v.iter().take(offset).chain(s.iter()).chain(v.iter().skip(offset));
    
    let result: Vec<_> = chain.collect();
    println!("Result: {:?}", result);
    
    0 讨论(0)
  • 2020-11-27 21:31

    As of Rust 1.21.0, Vec::splice is available and allows inserting at any point, including fully prepending:

    let mut vec = vec![1, 5];
    let slice = &[2, 3, 4];
    
    vec.splice(1..1, slice.iter().cloned());
    
    println!("{:?}", vec); // [1, 2, 3, 4, 5]
    

    The docs state:

    Note 4: This is optimal if:

    • The tail (elements in the vector after range) is empty
    • or replace_with yields fewer elements than range’s length
    • or the lower bound of its size_hint() is exact.

    In this case, the lower bound of the slice's iterator should be exact, so it should perform one memory move.


    splice is a bit more powerful in that it allows you to remove a range of values (the first argument), insert new values (the second argument), and optionally get the old values (the result of the call).

    Replacing a set of items

    let mut vec = vec![0, 1, 5];
    let slice = &[2, 3, 4];
    
    vec.splice(..2, slice.iter().cloned());
    
    println!("{:?}", vec); // [2, 3, 4, 5]
    

    Getting the previous values

    let mut vec = vec![0, 1, 2, 3, 4];
    let slice = &[9, 8, 7];
    
    let old: Vec<_> = vec.splice(3.., slice.iter().cloned()).collect();
    
    println!("{:?}", vec); // [0, 1, 2, 9, 8, 7]
    println!("{:?}", old); // [3, 4]
    
    0 讨论(0)
提交回复
热议问题