前言:今天重新学习了一下集合类,在进行练习时,对于List集合的contains方法产生了一些疑惑,记录一下,以防以后再产生类似疑惑。
首先,描述一下作为练习的题目:
键盘录入 Person信息 包括 姓名 年龄
1.当录入 姓名 输入为exit 结束输入
2.将Person对象 存入到集合中
如果 姓名 年龄 相同 视为同一个人 那么只存储一个对象(去重)
3.使用三种方式遍历集合 获取集合中 Person姓名 和 年龄 进行输出
我们首先需要创建一个Person JavaBean,用来进行对象属性的存储和获取。
class Person
{
private String name;
private int age;
Person(){}
Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public String toString(){
return "["+name+","+age+"]";
}
}
其中还重写了toString方法用于格式化输出对象的属性。
然后我们需要的就是重复创建多个对象并将其存入集合中去了,可以采取循环的方式,根据题目要求,需要实现键盘输入并当输入‘exit’时结束对象的创建,因此我们可以再一个死循环中进行对象的重复创建和存储,并判断用户的键入值是否为exit而跳出循环。
Scanner sc = new Scanner(System.in);
List list = new ArrayList();
for(;;){
String name = sc.next();
if(name.equals("exit")){
break;
}else{
int age = sc.nextInt();
list.add(new Person(name,age));//多态
}
}
这里暂且不为集合指定泛型,顺便练习一下多态的部分内容。
对象存储完成后,便是对存入对象的去重操作。第一眼看到这个问题,首先想到的是采用循环判断是否有重复信息进而对重复信息进行删除,之后仔细查看了一下集合的API发现可以采用方法完成去重的操作,比如contains方法。
contains方法字面意思是包含的意思,该方法的作用是判断一个集合中是否有指定的元素存在,因此,解题思路就出现了:我们可以创建一个新的集合,其中的元素是没有重复元素的。然后通过迭代器或其他遍历方法来获取原本集合中的每一个元素,在每次遍历出元素时用新集合调用contains方法进行判断新集合中是否包含该元素,如果包含就跳过该元素,继续遍历;如果不包含就将其添加到新集合中去。下面是实现代码:
public static ArrayList getSingle(List list){
ArrayList newList = new ArrayList();
Iterator it = list.iterator();
while(it.hasNext()){
Object obj = it.next();
if(!(newList.contains(obj))){
newList.add(obj);
}
}
return newList;
}
这样方法就写好了,在主方法直接调用就可以。但是当我们调用方法获取新数组,输出之后发现,重复元素并没有去掉。这是为什么呢?问题应该出在contains方法上,因此带着这个疑问我去看了contains方法的源码,源码如下:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/*
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index i such that
* (o==null ? get(i)==null : o.equals(get(i))),
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
我们可以看到,contains方法被调用时,会调用indexOf方法,而indexOf方法中首先判断了传入的元素是否为空,如果为空就在调用方法的集合中找为空的元素并返回角标;如果不为空就循环判断传入元素是否与某个元素相等,这时我们可以看到,它是根据equals方法进行判断的。果然问题就在这里,这里的equals方法时父类的equals方法,默认是比较两者的地址值。而我们调用方法的集合和传入的元素的地址值肯定是不相同的。所以contains方法会一直返回false,去重操作无法完成。那么知道了原因,如何解决呢?我们的Person的默认父类就是Object,并且我们在存放Person对象时的数组元素是Object类型,相当于父类引用指向子类对象,如果我们在Person类中重写了equals方法后,就符合了形成多态的三个前提条件,那么在contains调用equals方法进行判断时,会默认调用子类重写的equals方法,完成比较传入对象属性值的目的。那么接下来就是重写equals方法了。
public boolean equals(Object obj){
if(!(obj instanceof Person)){
return false;
}
Person p = (Person)obj;
//return this.getName().equals(p.getName())&&this.getAge()==p.getAge();
//此处比较String类型属性的时候不能使用==比较,必须使用equals方法
//比较,==号比较的是String的地址
return this.getName().equals(p.getName())&&this.getAge()==p.getAge();
}
注意重写代码中注释部分的内容。
到此为止,就完成了去重的操作。接下来的三种遍历方式就很简单了,
//迭代器遍历
public static void printList(List list){
Iterator it = list.iterator();
while(it.hasNext()){
Person p = (Person)it.next();
System.out.print(p.toString());
}
System.out.println();
}
//普通for循环
public static void printList2(List list){
for(int i = 0;i
完成。remove方法也可以完成该项功能,可以自己扩展一下。