在Vec的中间或开头有效插入或替换多个元素?

时间:2015-02-23 16:37:31

标签: rust

是否有任何直接的方式在线性时间内&[T]的中间或开头插入或替换Vec<T>和/或Vec中的多个元素?

我只能找到std::vec::Vec::insert,但这只是为了在O(n)时间插入一个元素,所以我显然不能在循环中调用它。

可以在该索引处split_offextend将新元素放入分割的左半部分,然后extend将后半部分放入第一个,但有更好的方法吗?

3 个答案:

答案 0 :(得分:7)

好的, Vec 界面中没有合适的方法(我可以看到)。但我们总能自己实施同样的事情。

的memmove

T 复制时,可能最明显的方法是移动内存,如下所示:

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());
            }
        },
    }
}

洗牌

如果 T 不是副本,但它实现克隆,我们可以将给定切片附加到 Vec 的末尾,然后移动它在线性时间内使用 swap s到所需位置:

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);
        },
    }
}

迭代器concat

也许最好的选择是不要修改 Vec 。例如,如果您要通过迭代器访问结果,我们可以从我们的块中构建迭代器链:

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);

答案 1 :(得分:6)

从Rust 1.21.0开始,Vec::splice可用并允许在任何时候插入,包括完全预先添加:

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]

文档声明:

  

注4:如果符合以下条件,则最佳:

     
      
  • 尾部(范围之后的向量中的元素)为空
  •   
  • replace_with产生的元素少于范围长度
  •   
  • 或其size_hint()的下限是完全正确的。
  •   

在这种情况下,切片迭代器的下限应该是精确的,因此它应该执行一次内存移动。

splice更强大一点,它允许你删除一系列值(第一个参数),插入新值(第二个参数),并可选择获取旧值(结果为呼叫)。

替换一组项目

let mut vec = vec![0, 1, 5];
let slice = &[2, 3, 4];

vec.splice(..2, slice.iter().cloned());

println!("{:?}", vec); // [2, 3, 4, 5]

获取以前的值

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]

答案 2 :(得分:-1)

我试图在锈中添加一个向量,并发现this closed question在此处链接,(尽管这个问题是两者都添加和插入AND效率。我想我的答案是更好地作为另一个更精确的问题的答案,因为我无法证明效率),但是以下代码帮助我成为了前提(反之亦然。)[我确信另外两个答案更有效,但是我的学习方式是,我喜欢拥有可以用示例来说明答案应用示例的答案。]

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]);
}