1.关于循环遍历
平时我们最常使用的两个List:
- ArrayList
- LinkedList
很熟悉的特点一个是数组实现,一个是链表
也就是一个能随机存取,一个只能顺序存取,所以在循环遍历时要十分注意:
Java中有大体三种遍历方式:
- 最基本for循环
- 迭代器遍历
- foreach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16List<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
10for (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
一个是基于数组,一个是基于链表,所以在选择上还是视具体情况而定