前言:本章将对Collection接口里面定义的所有方法进行一个详解。 1、
int size();
这个很简单,就是返回当前集合中存了几个元素
2、
boolean isEmpty();
返回集合中是否是空,也就是说有元素就返回false,没元素就返回true
3、
boolean contains(Object o);
查看集合中是否存在o这个元素。注意,contains中是依赖对象的equals方法来判断两个对象是否相等的。所以如果说我把一个类型的equals方法重写了,让它一直返回false,那么这个类型的集合中调用contains方法是无法找到的,也就是永远返回true。我们可以看下面代码:
public class Client
{
public static void main(String[] args)
{
test1();
}
private static void test1()
{
Collection<Student> stus= new ArrayList<Student>();
stus.add(new Student("yqs",3303));
stus.add(new Student("yqs",3304));
stus.add(new Student("dsb",3300));
System.out.println("是否存在:"+stus.contains(new Student("yqs",3309)));
System.out.println("是否存在:"+stus.contains(new Student("yqs",3304)));
}
private static class Student
{
String name;
int id;
@Override
public boolean equals(Object stu) {
return id==((Student)stu).id;
}
public Student(String name, int id) {
this.name = name;
this.id = id;
}
}
}
程序运行结果:
是否存在:false
是否存在:true
4、
Iterator<E> iterator();
这个方法返回一个迭代器对象,迭代器对象主要用来遍历集合里的元素。迭代器对象里面有一个next()方法,返回下一个元素(一开始指向第0个元素,也就是不存在的元素),所以我们第一次调用next()就会返回第一个元素,也就是"abc",第二次调用就会返回"qwer",以此类推,我们就可以利用这个迭代器对象遍历集合里面的元素了。下面放出迭代器对象的经典用法:
Collection<String> c= new ArrayList<String>();
c.add("abc");
c.add("qwer");
c.add("az");
Iterator<String> it=c.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
这样子就可以遍历了
5、
Object[] toArray();
这个函数是将集合里面的元素转成一个数组然后返回。用法如下:
public static void main(String[] args)
{
Collection<String> c= new ArrayList<String>();
c.add("abc");
c.add("qwer");
c.add("az");
Object[] arr= c.toArray();
for (Object s: arr)
{
System.out.println((String)s);
}
}
输出如下:
abc
qwer
az
这里有一个问题,既然我们知道元素是String类型的,那为什么不直接这样:
String[] arr= (String[])c.toArray();
原因是,我们人知道这个数组里面寸的都是字符串,但是编译器并不知道这件事情。那有人说
Collection<String> c= new ArrayList<String>();
我们定义的时候不是用泛型指定了数据类型了吗。但是我们仔细观察,发现我们的类型转化代码是写在main方法里面的,而不是集合内部,集合虽然知道我们的元素是String类型的,但是我们在main方法里的时候依然是不知道的。如果强行运行
String[] arr= (String[])c.toArray();
编译器会报这个错误: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.String; ([Ljava.lang.Object; and [Ljava.lang.String; are in module java.base of loader ‘bootstrap’)
我们知道,我们平时写代码是可以这样子类型转化的:
Object obj="123";
String str=(String)obj;
很显然上面代码是合法的,jvm也不会阻止你这样做(当然如果你的类型转化不合理jvm也会给你报一个运行时错误的)。 Object可以转String,那为什么到了Object[]数组就不行了呢?这里我给出一下自己的理解,如有问题,可以指正。就比如如果我的Object数组是这么定义的:
Object[] objects=new Object[]{"123",new Student(),52,'8',new Cat()};
懂了吧,Object数组里面又不是只会存一种数据类型,但是你转化的时候只能往一个类型去转,所以这样子转的不确定性和不稳定性很大,显然是不合理的,所以jvm干脆就不然你转了。有小伙伴会问,集合内部的Object数组肯定是存的同一种类型呀。但是你要明白,Object数组又不是只给你集合类用的,它在很多其它地方也有使用,所以jvm必须要考虑所有的情况。
6、
<T> T[] toArray(T[] a);
这个方法就是为了解决上面第5个方法的不足,我们可以在调用toArray的时候传入一个数组类型来显式的指定我们要什么类型的数据,这样子toArray内部就可以拿着这个类型,对元素一一进行的类型转化,最后返回数组。 所以说a这个参数就是用来指明我想要的类型。 如果a这个数组的长度大于等于元素的个数,那么方法内部会直接把元素添加到这个a上面,然后返回的那个数组就是传入的这个a数组。 如果小于的话,方法内部就会新建一个长度刚好的数组,然后存元素,然后返回。
7、
default <T> T[] toArray(IntFunction<T[]> generator)
{
return toArray(generator.apply(0));
}
这个的话稍微复杂一点,可能需要一点基础才能明白。 我们先看下 IntFunction这个接口的定义:
@FunctionalInterface
public interface IntFunction<R> {
R apply(int value);
}
其实接口很简单,就一个函数,也就是一个函数式接口,可以用来表示一个函数(具体可以百度函数式接口)。 我们向toArray方法传入一个函数,然后toArray方法内部会调用一次这个函数,传入的参数是0,个人理解这里传0还是传什么不重要,因为想要调用这个方法肯定是要传一个参数的。最后把这个函数的返回值传给我们上面第6个介绍的那个toArray中去,最后把返回值返回。具体怎么用呢,这里给出我个人的一个理解:
Collection<String> c= new ArrayList<String>();
c.add("abc");
c.add("qwer");
c.add("az");
String[] strings=c.toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value)
{
System.out.println("传入的value参数是:"+value);
return new String[c.size()];
}
});
for(String s:strings)
{
System.out.println(s);
}
输出是:
传入的value参数是:0
abc
qwer
az
或者可以直接这样:
Collection<String> c= new ArrayList<String>();
c.add("abc");
c.add("qwer");
c.add("az");
String[] strings=c.toArray(String[]::new);
for(String s:strings)
{
System.out.println(s);
}
输出是:
abc
qwer
az
这一块需要函数式接口的知识,大家可以自行学习。 那么这个方法和第6个那个方法有啥区别呢,个人理解是可能用起来会更放别简洁一点。
8、
boolean add(E e);
这个的话就很熟悉了,用来添加一个元素到集合中去,并且返回一个布尔值表示是否添加成功。前面的很多代码块都大量的用到了这个add方法,大家可以翻上去看看。
9、
boolean remove(Object o);
这个和add是对立的,add用来添加,这个用来删除。具体是删除集合中第一个和o相等的元素,如何判断是否相等呢,这里和上面的那个contains方法一致,都是依赖equals方法来判断的。下面给出一段测试代码:
public static void main(String[] args)
{
Collection<Student> c= new ArrayList<Student>();
c.add(new Student("yqs",3303));
c.add(new Student("yqs1",3304));
c.add(new Student("yqs2",3304));
c.add(new Student("yqs",3305));
c.remove(new Student("yqs",3304));
for (Student stu:c)
{
System.out.println(stu.name+" "+stu.id);
}
}
class Student
{
String name;
int id;
@Override
public boolean equals(Object stu) {
return id==((Student)stu).id;
}
public Student(String name, int id) {
this.name = name;
this.id = id;
}
}
执行结果是:
yqs 3303
yqs2 3304
yqs 3305
大家可以对着代码自行理解,应该不难。
10、
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
这几个其实都差不多,前面的是添加删除查找单个元素,这里的话就变成添加查找删除一个元素集。因为都差不多,所以直接写到一段代码里面去:
public static void main(String[] args)
{
Collection<Student> class1= Stream.of(
new Student("yqs1",11),new Student("yqs2",12),new Student("yqs3",13))
.collect(Collectors.toList());
Collection<Student> class2= Stream.of(
new Student("bbq1",21),new Student("bbq2",22),new Student("bbq3",23))
.collect(Collectors.toList());
Collection<Student> allStus= new ArrayList<Student>();
allStus.addAll(class1);
allStus.addAll(class2);
System.out.println("所有学生名单:");
for(Student stu:allStus)
{
System.out.println(stu.name+" "+stu.id);
}
Collection<Student> tempClass= Stream.of(
new Student("yqs1",11),new Student("bbq1",21),new Student("llc",99))
.collect(Collectors.toList());
System.out.println("所有学生里是否有所有临时班级的学生:"+allStus.containsAll(tempClass));
System.out.println("所有学生里是否有所有1班的学生:"+allStus.containsAll(class1));
allStus.removeAll(tempClass);
System.out.println("把临时班级和所有学生里共同存在的学生从所有学生中删除");
System.out.println("删除后的名单是:");
for(Student stu:allStus)
{
System.out.println(stu.name+" "+stu.id);
}
}
输出结果是:
所有学生名单:
yqs1 11
yqs2 12
yqs3 13
bbq1 21
bbq2 22
bbq3 23
所有学生里是否有所有临时班级的学生:false
所有学生里是否有所有1班的学生:true
把临时班级和所有学生里共同存在的学生从所有学生中删除
删除后的名单是:
yqs2 12
yqs3 13
bbq2 22
bbq3 23
这里我用了Stream.of来构建集合,因为这样子比一个个add要方便。感兴趣的小伙伴可以学习一下java的Stream操作。 一句话总结: addAll就是集合的并集操作,removeAll就是集合的作差,containsAll就是判断两个集合的交集是否属于第一个集合。
11、
default boolean removeIf(Predicate<? super E> filter)
也是函数式接口的内容,我们需要传入一个过滤器函数,告诉集合我需要删除哪些特征的元素,话不多说,上代码,下面代码就是用来删除名字以y开头的学生:
public static void main(String[] args)
{
Collection<Student> students= Stream.of(
new Student("yqs",11),new Student("yhh",12),new Student("zzz",13),
new Student("obj",14),new Student("yes",15),new Student("kbl",16))
.collect(Collectors.toList());
System.out.println("删除前:");
for(Student stu:students)
{
System.out.println(stu.name+" "+stu.id);
}
students.removeIf(new Predicate<Student>() {
@Override
public boolean test(Student student) {
return student.name.charAt(0)=='y';
}
});
System.out.println("删除后:");
for(Student stu:students)
{
System.out.println(stu.name+" "+stu.id);
}
}
输出结果:
删除前:
yqs 11
yhh 12
zzz 13
obj 14
yes 15
kbl 16
删除后:
zzz 13
obj 14
kbl 16
未完待续。。。
|