精华内容
下载资源
问答
  • list和map: 使用erase()函数时直接将迭代器作为实参传入,但这时erase()函数是有返回值的,删除要销毁的元素的指针,并且返回下一个元素的指针,所以要这样来写: itor = List.erase( itor );map同理。 ...

    vector:
    使用erase()函数时直接将迭代器作为实参传入即可,但要注意后面要加上一个语句:itor–;不然遍历时会漏掉一个元素。
    list和map:
    使用erase()函数时直接将迭代器作为实参传入,但这时erase()函数是有返回值的,删除要销毁的元素的指针,并且返回下一个元素的指针,所以要这样来写: itor = List.erase( itor );map同理。

    展开全文
  • stl vector/list如何一边遍历一边删除

    千次阅读 2012-11-01 12:51:52
    有时候我们在遍历stl的容器的时候需要删除一些不符合条件的item,这时候我们就要担心iterator...下面比较一下list vector的两种一边遍历一边删除: // list list lll; // vector // vector lll; lll.push_ba

    这篇文章的遍历删除的效率可能会比较低,更优化的办法请参见如下文章

    http://blog.csdn.net/wangeen/article/details/39473029


    有时候我们在遍历stl的容器的时候需要删除一些不符合条件的item,这时候我们就要担心iterator是不是因为原始的数据的改变而发生改变,因此往往比较容易出现一些问题,


    下面比较一下list 和 vector的两种一边遍历一边删除:
        // list
        list<int> lll;
        // vector
        // vector<int> lll;
       
        lll.push_back(1);
        lll.push_back(2);
        auto tb = lll.begin(), te= lll.end();
        for(;tb!=te;){  // not ++tb here!!!
            printf("%p", &*tb);
            printf("%p", &*(lll.end()));
            if(something()==true){
                tb=lll.erase(tb);
            }else{
                ++tb;
            }
        }
    


    list的能正常通过运行但是vector的却有断错误,问题出在哪里呢? 问题就是在于两个的erase函数的不同

    List:
    This effectively reduces the list size by the number of elements removed, calling each element's destructor before.

    lists are sequence containers specifically designed to be efficient inserting and removing elements in any position, even in the middle of the sequence. Compared to the other base sequence containers (vector and deque), lists are the most efficient container erasing at some position other than the beginning or the end of the sequence, and, unlike in these, all of the previously obtained iterators and references remain valid after the erasing operation and refer to the same elements they were referring before (except, naturally, for those referring to erased elements).

    vector:
    Because vectors keep an array format, erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions, which may not be a method as efficient as erasing in other kinds of sequence containers (deque, list).

    This invalidates all iterator and references to position (or first) and its subsequent elements.


    不难发现list erase不会改不原来的iterator, vector 就会改变,因此我们只要在代码里稍微做一点点修改就可以改正这个问题:
    for(;tb!=te;)
    
    // change to
    
    for(;tb!=lll.end();),
    

    另外我们也发现 vector 的 erase 需要整个vector 移动,这个代价十分高,所以尽量少用。若排序顺序不是很重要的化,可以和最后的那个item swap,然后删掉最后那个,这样可以显著的提高效率。

    展开全文
  • list的三种循环遍历方式 // int的类型 #include <iostream> #include <list> //using namespace std; int main() { //================================================================= std::...

    list的三种循环遍历方式

    // int的类型

    #include <iostream>
    #include <list>
    
    //using namespace std;
    
    int main() {
    
    //=================================================================
        std::list<int> ls;
    
        ls.push_back(120);
        ls.push_back(110);
        ls.push_front(130);	// list有push_front,vector没有push_front
        ls.push_front(140);
        ls.push_front(150);
        ls.push_back(100);
    
    
        for (int i = 0; i < ls.size(); ++i) {
    //        std::cout << ls[i] << std::endl;	// list不可以这么用,vector可以这么用
        }
    
        
        for (int ele:ls) {
            std::cout << "基于范围的for循环:" << ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
        
        
        std::list<int>::iterator ele;
        for (ele = ls.begin(); ele != ls.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << *ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        ls.pop_back();
        ls.pop_front();	// list有pop_front,vector没有pop_front
        
        ls.emplace_back(200);
        ls.emplace_front(300);
        
        return 0;
    }
    

    // float的类型

    #include <iostream>
    #include <list>
    
    //using namespace std;
    
    int main() {
    
    //=================================================================
        std::list<float> ls;
    
        ls.push_back(120.123);
        ls.push_back(110.123);
        ls.push_front(130.123);	// list有push_front,vector没有push_front
        ls.push_front(140.123);
        ls.push_front(150.123);
        ls.push_back(100.123);
    
    
        for (int i = 0; i < ls.size(); ++i) {   // TODO 这里i不要用float
    //        std::cout << ls[i] << std::endl;	// list不可以这么用,vector可以这么用
        }
    
    
        for (float ele:ls) {
            std::cout << "基于范围的for循环:" << ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    
        std::list<float>::iterator ele;
        for (ele = ls.begin(); ele != ls.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << *ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        ls.pop_back();
        ls.pop_front();	// list有pop_front,vector没有pop_front
    
        ls.emplace_back(200.123);
        ls.emplace_front(300.123);
        
        return 0;
    }
    

    // string的类型

    #include <iostream>
    #include <list>
    
    //using namespace std;
    
    int main() {
    
    //=================================================================
        std::list<std::string> ls;
    
        ls.push_back("CCC");
        ls.push_back("BBB");
        ls.push_front("DDD");	// list有push_front,vector没有push_front
        ls.push_front("EEE");
        ls.push_front("FFF");
        ls.push_back("AAA");
    
    
    //    for (std::string i = 0; i < ls.size(); ++i) {
            std::cout << ls[i] << std::endl;	// 不能这么用
    //    }
    
        
        for (std::string ele:ls) {
            std::cout << "基于范围的for循环:" << ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
        
        
        std::list<std::string>::iterator ele;
        for (ele = ls.begin(); ele != ls.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << *ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        ls.pop_back();
        ls.pop_front();	// list有pop_front,vector没有pop_front
    
        ls.emplace_back("XXX");
        ls.emplace_front("YYY");
        
        return 0;
    }
    

    // class的类型

    #include <iostream>
    #include <list>
    
    //using namespace std;
    
    class A {
    public:
        std::string name = "";
        int age = 0;
    
        A(std::string name_temp, int age_temp) : name(name_temp), age(age_temp) {
            std::cout << "执行了构造函数 " << name << std::endl;
        }
    
        ~A() {
            std::cout << "执行了析构函数 " << name << std::endl;
        }
    };
    
    
    int main() {
    
    //=================================================================
        A a1("张一", 10);
        A a2("张二", 20);
        A a3("张三", 30);
        A a4("张四", 40);
    
        std::list<A> ls;
    
        ls.push_front(a3);    // 这里也会执行构造函数创建对象
        ls.push_back(a2);
        ls.push_back(a1);
        ls.push_front(a4);	// list有push_front,vector没有push_front
    
    
    //    for (A i = 0; i < ls.size(); ++i) {
            std::cout << ls[i] << std::endl;	// 不能这么用
    //    }
    
        
        for (A ele:ls) {
            std::cout << "基于范围的for循环:" << ele.name << ele.age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
        
        std::list<A>::iterator ele;
        for (ele = ls.begin(); ele != ls.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << (*ele).name << (*ele).age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        ls.pop_back();
        ls.pop_front();	// list有pop_front,vector没有pop_front
    
        ls.emplace_back(A("张五", 50));
        ls.emplace_front(A("张六", 60));
        
        return 0;
    }
    

    // class的类型(指针)

    #include <iostream>
    #include <list>
    
    //using namespace std;
    
    class A {
    public:
        std::string name = "";
        int age = 0;
    
        A(std::string name_temp, int age_temp) : name(name_temp), age(age_temp) {
            std::cout << "执行了构造函数 " << name << std::endl;
        }
    
        ~A() {
            std::cout << "执行了析构函数 " << name << std::endl;
        }
    };
    
    
    int main() {
    
    //=================================================================
        A a1("张一", 10);
        A a2("张二", 20);
        A a3("张三", 30);
        A a4("张四", 40);
    
        std::list<A*> ls;		// 这里用了指针
    
        ls.push_front(&a3);    // 这里不会执行构造函数创建对象
        ls.push_back(&a2);
        ls.push_back(&a1);
        ls.push_front(&a4);		// list有push_front,vector没有push_front
    
    
    //    for (A* i = 0; i < ls.size(); ++i) {
            std::cout << ls[i] << std::endl;	// 不能这么用
    //    }
    
    
        for (A* ele:ls) {
            std::cout << "基于范围的for循环:" << ele->name << ele->age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    
        std::list<A*>::iterator ele;
        for (ele = ls.begin(); ele != ls.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << (*ele)->name << (*ele)->age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        ls.pop_back();
        ls.pop_front();	// list有pop_front,vector没有pop_front
        
        A a5("张五", 50);
        A a6("张六", 60);
        ls.emplace_back(&a5);
        ls.emplace_front(&a6);
        
        return 0;
    }
    

    vector的三种循环遍历方式

    // int的类型

    #include <iostream>
    #include <vector>
    
    //using namespace std;
    
    int main() {
    
    //=================================================================
        std::vector<int> v;
    
        v.push_back(120);
        v.push_back(110);
    //    v.push_front(130);	// list有push_front,vector没有push_front
    //    v.push_front(140);
    //    v.push_front(150);
        v.push_back(100);
    
    
        for (int i = 0; i < v.size(); ++i) {
            std::cout << v[i] << std::endl;	// list不可以这么用,vector可以这么用
        }
    
    
        for (int ele:v) {
            std::cout << "基于范围的for循环:" << ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    
        std::vector<int>::iterator ele;
        for (ele = v.begin(); ele < v.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << *ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        v.pop_back();
    //    v.pop_front();	// list有pop_front,vector没有push_front
        
        v.emplace_back(200);
    //    v.emplace_front(300);	// list有emplace_front,vector没有emplace_front
        
        return 0;
    }
    

    // float的类型

    #include <iostream>
    #include <vector>
    
    //using namespace std;
    
    int main() {
    
    //=================================================================
        std::vector<float> v;
    
        v.push_back(120.123);
        v.push_back(110.123);
    //    v.push_front(130.123);	// list有push_front,vector没有push_front
    //    v.push_front(140.123);
    //    v.push_front(150.123);
        v.push_back(100.123);
    
    
        for (int i = 0; i < v.size(); ++i) {   // TODO 这里i不要用float
            std::cout << v[i] << std::endl;	// list不可以这么用,vector可以这么用
        }
    
    
        for (float ele:v) {
            std::cout << "基于范围的for循环:" << ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    
        std::vector<float>::iterator ele;
        for (ele = v.begin(); ele < v.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << *ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        v.pop_back();
    //    v.pop_front();	// list有pop_front,vector没有push_front
            
        v.emplace_back(200.123);
    //    v.emplace_front(300.123);	// list有emplace_front,vector没有emplace_front
        
        return 0;
    }
    

    // string的类型

    #include <iostream>
    #include <vector>
    
    //using namespace std;
    
    int main() {
    
    //=================================================================
        std::vector<std::string> v;
    
        v.push_back("CCC");
        v.push_back("BBB");
    //    v.push_front("DDD");	// list有push_front,vector没有push_front
    //    v.push_front("EEE");
    //    v.push_front("FFF");
        v.push_back("AAA");
    
    
    //    for (std::string i = 0; i < v.size(); ++i) {
            std::cout << ls[i] << std::endl;	// 不能这么用
    //    }
    
    
        for (std::string ele:v) {
            std::cout << "基于范围的for循环:" << ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    
        std::vector<std::string>::iterator ele;
        for (ele = v.begin(); ele < v.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << *ele << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        v.pop_back();
    //    v.pop_front();	// list有pop_front,vector没有push_front
            
        v.emplace_back("XXX");
    //    v.emplace_front("YYY");	// list有emplace_front,vector没有emplace_front
        
        return 0;
    }
    

    // class的类型

    #include <iostream>
    #include <vector>
    
    //using namespace std;
    
    class A {
    public:
        std::string name = "";
        int age = 0;
    
        A(std::string name_temp, int age_temp) : name(name_temp), age(age_temp) {
            std::cout << "执行了构造函数 " << name << std::endl;
        }
    
        ~A() {
            std::cout << "执行了析构函数 " << name << std::endl;
        }
    };
    
    
    int main() {
    
    //=================================================================
        A a1("张一", 10);
        A a2("张二", 20);
        A a3("张三", 30);
        A a4("张四", 40);
    
        std::vector<A> v;
    
    //    v.push_front(a3);	// list有push_front,vector没有push_front
        v.push_back(a2);       // 这里也会执行构造函数创建对象
        v.push_back(a1);
    //    v.push_front(a4);
    
    
    //    for (A i = 0; i < v.size(); ++i) {
            std::cout << ls[i] << std::endl;	// 不能这么用
    //    }
    
    
        for (A ele:v) {
            std::cout << "基于范围的for循环:" << ele.name << ele.age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    
        std::vector<A>::iterator ele;
        for (ele = v.begin(); ele < v.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << (*ele).name << (*ele).age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        v.pop_back();
    //    v.pop_front();	// list有pop_front,vector没有push_front
            
        v.emplace_back(A("张五", 50));
    //    v.emplace_front(A("张六", 60));	// list有emplace_front,vector没有emplace_front
    
        return 0;
    }
    

    // class的类型(指针)

    #include <iostream>
    #include <vector>
    
    //using namespace std;
    
    class A {
    public:
        std::string name = "";
        int age = 0;
    
        A(std::string name_temp, int age_temp) : name(name_temp), age(age_temp) {
            std::cout << "执行了构造函数 " << name << std::endl;
        }
    
        ~A() {
            std::cout << "执行了析构函数 " << name << std::endl;
        }
    };
    
    
    int main() {
    
    //=================================================================
        A a1("张一", 10);
        A a2("张二", 20);
        A a3("张三", 30);
        A a4("张四", 40);
    
        std::vector<A*> v;		// 这里用了指针
    
    //    v.push_front(&a3);	// list有push_front,vector没有push_front
        v.push_back(&a2);       // 这里不会执行构造函数创建对象
        v.push_back(&a1);
    //    v.push_front(&a4);
    
    
    //    for (A* i = 0; i < v.size(); ++i) {
            std::cout << ls[i] << std::endl;	// 不能这么用
    //    }
    
    
        for (A* ele:v) {
            std::cout << "基于范围的for循环:" << ele->name << ele->age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    
        std::vector<A*>::iterator ele;
        for (ele = v.begin(); ele < v.end(); ++ele) {// list不可以用<,vector可以用!=和<
            std::cout << "基于容器的for循环:" << (*ele)->name << (*ele)->age << std::endl;
        }
        std::cout << "-----------------------------" << std::endl;
    
    //=================================================================
        v.pop_back();
    //    v.pop_front();	// list有pop_front,vector没有push_front
        
        A a5("张五", 50);
        A a6("张六", 60);
        v.emplace_back(&a5);
    //    v.emplace_front(&a6);	// list有emplace_front,vector没有emplace_front
        
        return 0;
    }
    
    展开全文
  • 数组和List类似,也是有序结构,如果我们使用数组,在添加和删除元素的时候,会非常不方便。例如,从一个已有的数组{'A', 'B', 'C', 'D', 'E'}中删除索引为2的元素: ┌───┬───┬───┬───┬───┬...
    • List

    在集合类中,List是最基础的一种集合:它是一种有序列表,元素可重复。

    List的行为和数组几乎完全相同:List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置,List的索引和数组一样,从0开始。

    数组和List类似,也是有序结构,如果我们使用数组,在添加和删除元素的时候,会非常不方便。例如,从一个已有的数组{'A', 'B', 'C', 'D', 'E'}中删除索引为2的元素:

    ┌───┬───┬───┬───┬───┬───┐
    │ A │ B │ C │ D │ E │   │
    └───┴───┴───┴───┴───┴───┘
                  │   │
              ┌───┘   │
              │   ┌───┘
              │   │
              ▼   ▼
    ┌───┬───┬───┬───┬───┬───┐
    │ A │ B │ D │ E │   │   │
    └───┴───┴───┴───┴───┴───┘
    

    这个“删除”操作实际上是把'C'后面的元素依次往前挪一个位置,而“添加”操作实际上是把指定位置以后的元素都依次向后挪一个位置,腾出来的位置给新加的元素。这两种操作,用数组实现非常麻烦。因此,在实际应用中,需要增删元素的有序列表,我们使用最多的是ArrayList。实际上,ArrayList在内部使用了数组来存储所有元素。例如,一个ArrayList拥有5个元素,实际数组大小为6(即有一个空位):

    size=5
    ┌───┬───┬───┬───┬───┬───┐
    │ A │ B │ C │ D │ E │   │
    └───┴───┴───┴───┴───┴───┘
    

    当添加一个元素并指定索引到ArrayList时,ArrayList自动移动需要移动的元素:

    size=5
    ┌───┬───┬───┬───┬───┬───┐
    │ A │ B │   │ C │ D │ E │
    └───┴───┴───┴───┴───┴───┘
    

    然后,往内部指定索引的数组位置添加一个元素,然后把size1

    size=6
    ┌───┬───┬───┬───┬───┬───┐
    │ A │ B │ F │ C │ D │ E │
    └───┴───┴───┴───┴───┴───┘
    

    继续添加元素,但是数组已满,没有空闲位置的时候,ArrayList先创建一个更大的新数组,然后把旧数组的所有元素复制到新数组,紧接着用新数组取代旧数组:

    size=6
    ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
    │ A │ B │ F │ C │ D │ E │   │   │   │   │   │   │
    └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    

    现在,新数组就有了空位,可以继续添加一个元素到数组末尾,同时size1

    size=7
    ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
    │ A │ B │ F │ C │ D │ E │ G │   │   │   │   │   │
    └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    

    可见,ArrayList把添加和删除的操作封装起来,让我们操作List类似于操作数组,却不用关心内部元素如何移动。

    我们考察List<E>接口,可以看到几个主要的接口方法:

    • 在末尾添加一个元素:void add(E e)
    • 在指定索引添加一个元素:void add(int index, E e)
    • 删除指定索引的元素:int remove(int index)
    • 删除某个元素:int remove(Object e)
    • 将指定的索引上的元素修改成指定值:set(int index,Object obj)
    • 获取指定索引的元素:E get(int index)
    • 获取链表大小(包含元素的个数):int size()

    下面进入List的代码示例,我们可以看到以上这些方法的使用:

    public class ListDemo1 {
    
    	//List:特点是数据可重复 有序(list中有了索引 我们可以通过索引添加元素 修改元素  移除元素  得到元素)  
    	public static void main(String[] args) {
    		//创建List集合对象
    		List list1=new ArrayList();
    		//因为List是Collection的子接口   所以Collection的方法对它同样适用
    //		list1.add("zhangsan");
    //		list1.isEmpty();
    //		list1.clear();
    //		list1.remove("zhangsan");
    //		list1.contains("zhangsan");
    
            //除了使用ArrayList和LinkedList,我们还可以通过List接口提供的of()方法,根据给定元素快速创建List
            List<Integer> list2 = List.of(1, 2, 5);
            //但是List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常。
    		
    		list1.add(1);
            list1.add(null);
    		list1.add(1);
    		list1.add(2);
    		list1.add(3);
    		list1.add(4);
    		System.out.println(list1);
            
            /*补充:  int indexOf (Object o)
            返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 - 1。
            int lastIndexOf (Object o)
            返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 - 1。*/
            int index = list1.indexOf(1);
            System.out.println(index);
            int index1 = list2.lastIndexOf(4);
            System.out.println(index1);
            
    		/*补充:
            List还提供了boolean contains(Object o)方法来判断List是否包含某个指定元素*/
            System.out.println(list1.contains("C")); // false
            System.out.println(list2.contains("5")); // true
    		
    
    		//List 特有的方法
    		//1、向指定下标(索引) 添加元素 从0开始   (把小明内容添加到list1集合中1索引的位置)
    		list1.add(1, "小明");
    		list1.add(2, "张三");
    		System.out.println(list1);
    		//2、将指定这个索引的值 修改为指定元素内容  (将list1集合中1索引的内容修改成了王五)
    		list1.set(1, "王五");
    //collection中的移除方法 通过传入元素本身 去移除元素内容 返回布尔类型 移除成功返回true 否则false
    		System.out.println("collection中remove list中继承了"+list1.remove("小明"));
    		//3、删除指定索引值的元素 (把list1 1下标的内容移除   当我们在调用这个方法的时候返回的移除元素内容本身)
    		System.out.println("list特有==="+list1.remove(1));
    		System.out.println(list1);
    		
    		//4、通过索引值 返回该索引上元素的内容
    		System.out.println(list1.get(0));
    		
    		
    	}
    }
    • List遍历

    以代码演示List遍历的几种方式:

    //List集合遍历
    public class ListDemo2 {
    
    	public static void main(String[] args) {
    		List list1=new ArrayList();
    		list1.add("1");
    		list1.add("3");
    		list1.add("1");
    		list1.add("5");
    		list1.add("1");
    		fun1(list1);
    		fun2(list1);
    		fun3(list1);
    		
    
    	}
    	//第一种遍历方式  转换成数组 toArray
    	
    	//第二种foreach 
    	public static void fun1(List list) {
    		for (Object obj: list) {
    			System.out.println("foreach======"+obj);
    		}
        }
    	//第三种 可以使用for循环 collection对象不能使用for循环  而list可以使用for循环进行遍历集合
    	public static void fun2(List list) {
    		for(int i=0;i<list.size();i++) {
    			System.out.println("for循环 内容====="+list.get(i));
    		}
    	}
    	//第四种 迭代器
    	public static void fun3(List list) {
    		//第一步 得到迭代器
    		Iterator iterator = list.iterator();
    		//第二步 结合while 开始遍历 注意这里采用的是正向迭代
    		while(iterator.hasNext()) {
    			System.out.println("迭代器遍历======"+iterator.next());
    		}
            //你当然也可以实现反向迭代 自己试试吧
    	}
    
    }
    

    这里要说明一下,尽管我们要遍历一个List,完全可以用for循环根据索引配合get(int)方法遍历,也就是第三种方式,但这种方式并不推荐,一是代码复杂,二是因为get(int)方法只有ArrayList的实现是高效的,换成LinkedList后,索引越大,访问速度越慢。

    所以我们要始终坚持使用迭代器Iterator来访问ListIterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。Iterator对象知道如何遍历一个List,并且不同的List类型,返回的Iterator对象实现也是不同的,但总是具有最高的访问效率。

    Iterator对象有两个方法:boolean hasNext()判断是否有下一个元素,E next()返回下一个元素。因此,使用Iterator遍历List代码如下:

    import java.util.Iterator;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<String> list = List.of("apple", "pear", "banana");
            for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
                String s = it.next();
                System.out.println(s);
            }
        }
    }

     有人可能觉得使用Iterator访问List的代码比使用索引更复杂。但是,要记住,通过Iterator遍历List永远是最高效的方式。并且,由于Iterator遍历是如此常用,所以,Java的for each循环本身就可以帮我们使用Iterator遍历。把上面的代码再改写如下:

    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<String> list = List.of("apple", "pear", "banana");
            for (String s : list) {
                System.out.println(s);
            }
        }
    }

    上述代码就是我们编写遍历List的常见代码。

    实际上,只要实现了Iterable接口的集合类都可以直接用for each循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each循环变成Iterator的调用,原因就在于Iterable接口定义了一个Iterator<E> iterator()方法,强迫集合类必须返回一个Iterator实例。 

    • List和Array的转换

     把List变为Array有三种方法,第一种是调用toArray()方法直接返回一个Object[]数组:

    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<String> list = List.of("apple", "pear", "banana");
            Object[] array = list.toArray();
            for (Object s : array) {
                System.out.println(s);
            }
        }
    }

    这种方法会丢失类型信息,所以实际应用很少。

    第二种方式是给toArray(T[])传入一个类型相同的ArrayList内部自动把元素复制到传入的Array中:

    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            List<Integer> list = List.of(12, 34, 56);
            Integer[] array = list.toArray(new Integer[3]);
            for (Integer n : array) {
                System.out.println(n);
            }
        }
    }
    /*注意到这个toArray(T[])方法的泛型参数<T>并不是List接口定义的泛型参数<E>,所以,我们实际上可以传入其他类型的数组,例如我们传入Number类型的数组,返回的仍然是Number类型:
    public class Main {
        public static void main(String[] args) {
            List<Integer> list = List.of(12, 34, 56);
            Number[] array = list.toArray(new Number[3]);
            for (Number n : array) {
                System.out.println(n);
            }
        }
    }
    */

     但是,如果我们传入类型不匹配的数组,例如,String[]类型的数组,由于List的元素是Integer,所以无法放入String数组,这个方法会抛出ArrayStoreException

    如果我们传入的数组大小和List实际的元素个数不一致怎么办?根据List接口的文档,我们可以知道:

    如果传入的数组不够大,那么List内部会创建一个新的刚好够大的数组,填充后返回;如果传入的数组比List元素还要多,那么填充完元素后,剩下的数组元素一律填充null

    实际上,最常用的是传入一个“恰好”大小的数组:

    Integer[] array = list.toArray(new Integer[list.size()]);

     

    反过来,把Array变为List就简单多了,通过List.of(T...)方法最简单:

    Integer[] array = { 1, 2, 3 };
    List<Integer> list = List.of(array);

    对于JDK 11之前的版本,可以使用Arrays.asList(T...)方法把数组转换成List

    要注意的是,返回的List不一定就是ArrayList或者LinkedList,因为List只是一个接口,如果我们调用List.of(),它返回的是一个只读List,对只读List调用add()remove()方法会抛出UnsupportedOperationException

    import java.util.List;
    public class Main {
        public static void main(String[] args) {
            List<Integer> list = List.of(12, 34, 56);
            list.add(999); // UnsupportedOperationException
        }
    }
    
     
    • ArrayList

    ArrayList 是List的一个实现子类,从第一节关于数据结构的简要讨论中,我们也可以看到ArrayList底层数据结构是数组,是线程不安全的,关于它添加元素的细节也得知了,它采用顺序存储,并且是非同步的,允许相同元素和null,实现了动态大小的数组,遍历效率高,使用频率高,但是插入类操作慢。

    //ArrayList:数组实现  顺序存储    遍历速度快  插入速度慢
    public class ArrayListDemo {
    
    	public static void main(String[] args) {
    		//创建arraylist集合对象 ArrayList 用法与List相同  方法同样适用
    		ArrayList al =new ArrayList();
    		al.add("123");
    		al.add("123");
    		al.add("123");
    		al.add("123");
    		al.add(1, "ddd");
            System.out.println(al);
    
            //列表迭代器 也可以指定从哪个下标开始  也可以不指定 ==》无参   
    		ListIterator listIterator = al.listIterator(1);
    		
            //使用列表迭代器对ArrayList进行正序和倒序遍历(即正向和反向迭代)
            
            //反向迭代
              /*
              ListIterator 中的特有的方法 可以进行反向迭代
              boolean hasPrevious ()
              如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
              E previous ()
              返回列表中的前一个元素。*/
            
            //但是只有先进行正向迭代,才能进行反向迭代  因为此时使用的是同一个迭代器对象
    
    
    		while(listIterator.hasNext()) {
    			System.out.println("正序"+listIterator.next());
    		}
    		//正序while 循环结束后  光标在最后一个元素后面的位置
    		//注意光标位置
    		while(listIterator.hasPrevious()) {
    			System.out.println("倒序"+listIterator.previous());
    		}
    
    	}
    
    }
    • LinkedList 

     实现List接口并非只能通过数组(即ArrayList的实现方式)来实现,另一种LinkedList通过“链表”也实现了List接口。在LinkedList中,它的内部每个元素都指向下一个元素:

            ┌───┬───┐   ┌───┬───┐   ┌───┬───┐   ┌───┬───┐
    HEAD ──>│ A │ ●─┼──>│ B │ ●─┼──>│ C │ ●─┼──>│ D │   │
            └───┴───┘   └───┴───┘   └───┴───┘   └───┴───┘

    LinkedList采取节点实现链式存储( 数据结构为双链表结构),即是说每个元素都存储在一个节点中,节点除了存储元素本身之外,还需要存储下一个元素的内存地址,以及上一个元素的内存地址。

    来看源码,我们主要看LinkedList是如何实现链表的:

    //链表的长度
        transient int size = 0;
     
        /**
         * Pointer to first node.
         * Invariant: (first == null && last == null) ||
         *            (first.prev == null && first.item != null)
         */
        //链表的头
        transient Node<E> first;
     
        /**
         * Pointer to last node.
         * Invariant: (first == null && last == null) ||
         *            (last.next == null && last.item != null)
         */
        //链表的尾
        transient Node<E> last;
        //链表维护的Node的结构
        private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;
     
            Node(Node<E> prev, E element, Node<E> next) {
                this.item = element;
                this.next = next;
                this.prev = prev;
            }
        }
    

    由此,我们可以看到,LinkedList维护的是一个first和last指针,而每个节点有item自身、prev和next两个节点来维护双链表的关系。

    它的特点是:

    • 查询速度慢   :需要根据前面的节点来获取后一个节点地址,查询时要将前面的所有节点都需访问一遍,节点数量越多,查询速度就越慢
    • 增删插速度快:增删一个元素,只需要修改新增元素前后两个节点引用 ,与集合本身元素个数无关,其中,在链表首尾添加元素很高效,在中间添加元素比较低效,首先要找到插入位置的节点,再修改前后节点的指针。

    下面以代码演示LinkedList的基本方法:

    public class LinkedListDemo {
    
    	public static void main(String[] args) {
    		
    		LinkedList ll =new LinkedList<>();
    		ll.add("dd");
    		//添加元素到集合最后一个索引位置
    		ll.addLast("dd1");
    		//添加元素到集合第一个索引位置
                    ll.addFirst("dd2");
    
                    //打印的结果为[dd2,dd,dd1]
                    
                    //得到第一个元素
    		System.out.println(ll.getFirst());
                    //得到最后一个元素
    		System.out.println(ll.getLast());
    	
                    //移除第一个元素
    		ll.removeFirst();
    		        //移除最后一个元素
    		ll.removeLast();
    		System.out.println(ll);
    		//其他方法跟List 一样使用
        }
    }

    关于 LinkedList的更多知识及方法操作,我看到一篇博文讲的非常详细,尤其是细致的讨论了节点为null以及越界的问题,在这里分享给大家:

    https://www.cnblogs.com/yijinqincai/p/10964188.html

    • Vector

    1.jdk1.0版本出现的。现在这个类已经过时,在jdk1.2后被ArrayList取代

    2.特点:

    • 线程安全  执行效率低
    • 顺序存储  增删较慢

    3.特有方法: 

    • addElement() 添加元素
    • removeElement() 移除元素
    • elements() 用于遍历

     

    • 对比

    Collection
    ├List (有序集合,允许相同元素和null)
     ├LinkedList (非同步,允许相同元素和null,遍历/查询效率低,插入和删除效率高)
     ├ArrayList (非同步,允许相同元素和null,实现了动态大小的数组,遍历/查询效率高,增删效率低,用的多)
     └Vector(同步,允许相同元素和null,底层数据结构是数组,遍历/查询快,增删慢,效率低)
     └Stack(继承自Vector,实现一个后进先出的堆栈)

    这里的同步又可以理解为线程安全。既然线程不安全为什么要使用?因为这个ArrayList比线程安全的Vector效率高。

    关于为什么说ArrayList是线程不安全的呢?这篇博文的分享值得参考:

    https://blog.csdn.net/u010416101/article/details/88720974?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

     

    创作不易,您的点赞评论转发是我写作的最大动力!

     

    展开全文
  • 关联容器(Associative Container):通过键(key)存储读取元素的(vector,list,deque,stack,queue,heap,priority_queue,slist.) 顺序容器(Sequential Container):通过元素在容器中的位置顺序存储访问元素。...
  • map遍历元素删除

    2019-09-23 07:02:11
    map遍历删除元素与vector和list遍历删除不一样: 直接上代码: template<typename T>void vecDelElem(vector<T> *vect, T elem)vector遍历删除正确方法: 1 { 2 typename vector<T>::...
  • 总结(1)对于ArrayListLinkedList,在size小于1000时,每种方式的差距都在几ms之间,差别不大,选择哪个方式都可以。(2)对于ArrayList,无论size是多大,差距都不大,选择哪个方式都可以。(3)对于LinkedList,当...
  • 选择这三种容器比较是因为他们都是顺序容器,且功能较为相似,都实现了List接口,可以使用其双向迭代器ListIterator。 构造函数name那里有个字符串的加法,让速度差距更加明显。 也说明了在时间复杂度渐进趋势相同...
  • vector和list的使用

    2017-09-22 15:15:43
    vector和list的区别 1.在存储中 vector是连续存储的;list中的对象不一定是连续存储的。 2.list如果要随机访问某个元素需要遍历list,没有效率。vector重载了[ ]可以直接访问。 3.在list中插入元素,尤其是在首尾...
  • vector和list的区别

    2020-04-18 17:42:57
    1)vector类似于数组,内存空间连续,list是双向链表,内存空间不要求连续 2)vector从中间或者前面插入元素效率低,list插入效率高 3)vector当内存不够时,会重新找一块内存...但是list需要从头重新遍历找下去。 ...
  • List逆向遍历、反向遍历--Iterator详解

    万次阅读 2017-12-26 20:26:58
    但是java集合中还有一个迭代器ListIterator,在使用List、ArrayList、LinkedList和Vector的时候可以使用。这两种迭代器有什么区别呢?下面我们详细分析。这里有一点需要明确的时候,迭代器指向的位置是元素之前的位置...
  • 1、vector中删除满足某些条件的元素迭代器失效问题#include #include <vector> using namespace std; int main() { std::vector<int> mVector; mVector.push_back(1); //插入元素push_back mVector.push_back...
  •  在学习stl的时候,总是有同学分不清楚Vector和List的使用,在这里我总结一下它们的区别和使用方法。 一、底层结构  vector的底层结构是动态顺序表,在内存中是一段连续的空间。  list的底层结构是带头节点的双向...
  • 笔记7:vector和list

    2020-03-15 17:56:26
    底层实现   vector的底层结构是动态顺序表,在内存中是一段连续的空间。...  list不支持随机访问,要想访问list中的某个元素只能是从前向后或从后向前依次遍历,时间复杂度是O(N)。 插入删除 ...
  • STL中vector和list的使用

    2017-09-03 09:33:19
    意向书组车一样,vetctor为它们的元素使用连续的存储位置,这意味着vector的元素也可以使用对其元素的常规指针的偏移来访问,但是与list不同,它们的大小可以动态的改变,其存储由容器自动处理. vector擅长的方面 1. ...
  • 第16章 List集合的总结和遍历 1.重构设计 根据Vector类,ArrayList类,LinkedList类所具有的存储特点以及拥有的方法入手,发现共性往上抽取。 共同特点: 1.允许元素重复 2.会记录先后添加的顺序po 根据他们的特点...
  • 迭代器的理解: 1.erase()函数的返回值,它的迭代器在循环遍历中的奇特之处; 1 #define _CRT_SECURE_NO_WARNINGS 2 #include <iostream> 3 #include <vector> 4 #include <list> 5 #...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 927
精华内容 370
关键字:

vector和list遍历