Java List集合使用小结

1.关于循环遍历

平时我们最常使用的两个List:

  • ArrayList
  • LinkedList

很熟悉的特点一个是数组实现,一个是链表
也就是一个能随机存取,一个只能顺序存取,所以在循环遍历时要十分注意:
Java中有大体三种遍历方式:

  • 最基本for循环
  • 迭代器遍历
  • foreach
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    List<Integer> list = new ArrayList<>();
    list.add(5);
    list.add(23);
    list.add(42);
    for (int i = 0; i < list.size(); i++) {
    System.out.print(list.get(i) + ",");
    }

    Iterator it = list.iterator();
    while (it.hasNext()) {
    System.out.print(it.next() + ",");
    }

    for (Integer i : list) {
    System.out.print(i + ",");
    }

    关于foreach:

    注意,他可不只是比for方便一点,他的实现上是跟for循环是有很大区别的
    其实for each循环内部也是依赖于Iterator迭代器,只不过Java提供的语法糖,Java编译器会将其转化为Iterator迭代器方式遍历。对以下for each循环进行反编译:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     for (Integer i : list) {
    System.out.println(i);
    }

    反编译后:

    Integer i;
    for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
    i = (Integer)iterator.next();
    }

    foreach使用限制:

  • 适用对象:遍历数组、集合(实现了Iterator接口)、Iterable对象
  • 在用foreach循环遍历一个集合时不能向集合中增加元素,不能从集合中删除元素,否则会抛出ConcurrentModificationException异常。抛出该异常是因为在集合内部有一个modCount变量用于记录集合中元素的个数,当向集合中增加或删除元素时,modCount也会随之变化,在遍历开始时会记录modCount的值,每次遍历元素时都会判断该变量是否发生了变化,如果发生了变化则抛出ConcurrentModificationException异常,所以这时如果想改变,就老老实实用Iterator对象去遍历,用Iterator的方法去增删
    1
    2
    3
    4
    5
    6
    7
    正确用法
    Iterator<Student> stuIter = students.iterator();
    while (stuIter.hasNext()) {
    Student student = stuIter.next();
    if (student.getId() == 2)
    stuIter.remove();//这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException
    }
  • 当使用foreach循环基本类型时变量时不能修改集合中的元素的值,遍历对象时可以修改对象的属性的值,但是不能修改对象的引用
    修改基本类型的值(原集合中的值没有变化,因为str是集合中变量的一个副本,只是一个句柄)
  • 最后一点时很容易看出的,foreach循环没有index,也就是没有下标,如果需要就还是用for把

选择:

当用linkedlist的时候,肯定要用后面两种遍历方法,第一种直接for循环效率极低

参考:
Java中的增强for循环(for each)的实现原理与坑
java中for和foreach的区别

2.关于java中的堆栈stack和队列实现的选取

不难发现java中也有stack直接实现类:Stack<T> stack = new Stack<>()
但是当你点进去看源码注释的时候发现:

A more complete and consistent set of LIFO stack operations isprovided by the {@link Deque} interface and its implementations, whichshould be used in preference to this class. For example: {@code Deque stack = new ArrayDeque();}

官方告诉我们有更好的实现,就是用deque,至于为什么弃用stack,可参考一下网上结论(大致就是stack设计得不严谨,不规范)

Stack是继承自Vector,Vector是由数组实现的集合类,他包含了大量集合处理的方法。而Stack之所以继承Vector,是为了复用Vector中的方法,来实现进栈(push)、出栈(pop)等操作。这里就是Stack设计不好的地方,既然只是为了实现栈,不用链表来单独实现,而是为了复用简单的方法而迫使它继承Vector,Stack和Vector本来是毫无关系的。这使得Stack在基于数组实现上效率受影响,另外因为继承Vector类,Stack可以复用Vector大量方法,这使得Stack在设计上不严谨

所以既然这样,那就用deque来做吧,deque是一个双向队列,完全具备普通队列FIFO的功能,同时它也具备了Stack的LIFO功能,并且保留了push和pop函数,所以使用起来应该是一点障碍都没有。
这里我想说一下他的两个实现:

  • LinkedList
  • ArrayDeque

一个是基于数组,一个是基于链表,所以在选择上还是视具体情况而定

© 2020 WPY's Android Tour All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero