这个我没学过,每次遇到都有点懵,今天补补课,好记性不如烂博客~
因为数组存储引用数据类型时有局限性,比如不能自动增长,为了方便封装了一些集合类供使用。
数组与集合的区别
数组可以存储基本数据类型也可以存储引用类型,集合只可以存引用数据类型(对象),但是利用JDK1.5自动装箱的特性,基本数据类型会被自动转换成对象,所以不影响。
数组长度是不可变的,集合的长度是可变的,可以随着元素的增加而增长(有的还是通过数组实现的,重新new一个1.5倍的数组,然后把原来数组中的数据存进去,原来的数组变成垃圾,1.5倍增长是一个折中的办法为了避免频繁地产生垃圾)。
集合继承体系图
这些集合名字左边表示通过算法什么实现,右边表示派别(除了Vector),Vector是最早有的集合JDK1.0就有了,和ArrayList很像,Vector是线程安全的,ArrayList是线程不安全的。
ArrayList是通过数组实现,LinekedList是通过链表实现。
Collection就是所有集合的根接口。
Collection的常用方法:add、clear、contains、equals、isEmpty、addAll
常规操作: 父类引用指向子类对象,通过add添加元素,clear清空。
clear()方法中从此集合中删除所有元素,只是把引用的指向删除了,集合中不再指向那些对象了,那些对象在堆中会变成垃圾,随后被垃圾回收机制回收。
Collection coll = new ArrayList();
coll.add("a");
coll.add("b");
System.out.println(coll);
coll.clear();
System.out.println(coll);
运行结果:
[a, b]
[]
contains方法判断是否包含指定元素,equals方法不是集合与元素之间的比较,而是集合与集合之间的比较。(List是有序的,相同元素顺序不同是不相等的)
isEmpty()方法判断集合是否为空。
Collection col1 = new ArrayList();
col1.add("a");
col1.add("b");
System.out.println(col1.contains("a"));//是否包含该元素
Collection col2 = new ArrayList();
col2.add("b");
col2.add("a");
System.out.println(col2.equals(col1));//是否相等
运行结果:
true
false
remove方法用于移除指定元素,并且返回boolean表示移除成功与否。
size方法获取集合中元素个数
Collection col1 = new ArrayList();
col1.add("a");
col1.add("b");
col1.remove("a");
System.out.println(col1);
System.out.println(col1.remove("c"));//是否移除元素成功
运行结果:
[b]
false
Collection的高级方法:addAll、containsAll、removeAll、retainAll
Collection col1 = new ArrayList();
col1.add("a");
col1.add("b");
Collection col2 = new ArrayList();
col2.add("c");
col2.add("d");
col1.add(col2);
System.out.println(col1);
System.out.println(col1.size());
运行结果:
[a, b, [c, d]]
3
add方法在添加的是另一个集合的时候会把整个集合当做一个元素填入。
如果想要把另一个集合所有元素加到这个集合中,需要用的是addAll方法:
Collection col1 = new ArrayList();
col1.add("a");
col1.add("b");
Collection col2 = new ArrayList();
col2.add("c");
col2.add("d");
col1.addAll(col2);
System.out.println(col1);
System.out.println(col1.size());
运行结果:
[a, b, c, d]
4
containsAll方法用于比较调用的集合是否包含传入的集合:
Collection col1 = new ArrayList();
col1.add("a");
col1.add("b");
Collection col2 = new ArrayList();
col2.add("c");
col2.add("b");
System.out.println(col1.containsAll(col2));
运行结果:false
removeAll删除的是两个集合的交集,一个元素都没有被删除,返回false。
col1.add("a");
col1.add("b");
Collection col2 = new ArrayList();
col2.add("c");
col2.add("b");
System.out.println(col1.removeAll(col2));
System.out.println(col1);
运行结果:
true
[a]
retainAll用来取两个集合的交集。当调用的集合元素没有被改变,返回false。
Collection col1 = new ArrayList();
col1.add("a");
col1.add("b");
Collection col2 = new ArrayList();
col2.add("c");
col2.add("d");
System.out.println(col1.retainAll(col2));
System.out.println(col1);
运行结果:true
[]
iterator迭代器遍历集合的方法
如果要遍历集合,可以转为数组再遍历,但是这样有点麻烦。可以封装好的函数iterator获取iterator迭代器来遍历集合。
Collection col1 = new ArrayList();
col1.add("a");
col1.add("b");
col1.add("c");
col1.add("d");
Iterator it = col1.iterator();//获取iterator迭代器
while (it.hasNext()){
Object obj1 = it.next();//使用it.next()元素就会往后移一位,一次循环应该只用一次
System.out.println(obj1);
}
运行结果:
a
b
c
d
或者使用for循环(可以节省点内存,循环结束以后迭代器就会被释放):
for (Iterator i = col1.iterator();i.hasNext();){
Object obj1 = i.next();
System.out.println(obj1);
}
void forEachRemaining(Consumer action),这是Java 8为Iterator新增的默认方法,该方法可使用Lambda表达式来遍历集合元素。
List l = new ArrayList();
l.add(1);
l.add(2);
ListIterator lI = l.listIterator();
lI.forEachRemaining(str->{
System.out.println(str);
});
Iterator迭代器的原理
为什么iterator要设置成接口,不直接在iterator类实现呢?因为collection底下的子类(例如ArrayList类、LinkedList类)他们的数据存储结构和迭代方式是不一样的,所以要设置成接口让他们用内部类各自实现。
那为什么不直接在这些子类里面直接定义hasNext()、next()这些方法呢,因为这样做的话整个体系中的类都要定义这些方法,过于臃肿,而且用接口能保证整个集合体系的遍历方式都是next和hasNext。
Iterator接口里面有四个抽象方法(JDK1.8版本):
要调用这些方法肯定有子类对他们进行重写,事实上就是子类中的内部类重写了这些方法,比如说在上一小节的例子中,就是ArrayList类中的内部类Itr继承了Iterator接口,重写了这些方法。
调用iterator()获取迭代器时,表面上(编译时)调用的是collection中的方法,实际上(运行中)调用的是collection的子类ArrayList类中的方法(因为创建对象时父类引用了子类对象,Collection col1 = new ArrayList();),在ArrayList类中,iterator()方法会创建 继承自Iterator的内部类Itr的一个对象,并且返回这个对象。
后续调用的hasNext()、next()等方法,运行时都是调用的ArrayList内部类Itr的方法,编译时调用的是Iterator这个接口中的方法。