目录
一、概述
我们知道java中常常需要对多个对象进行存储和操作,而使用array数组存储对象时有一些弊端(数组中提供的属性和方法少,不便于进行添加、删除、插入等操作)。因此集合应运而生(虽然集合底层也和数组密切相关)
二、集合框架继承树
Java 集合可分为 Collection 和 Map 两种体系
Collection接口:单列数据,定义了存取一组对象的方法的集合
List:元素有序、可重复的集合
Set:元素无序、不可重复的集合 (无序指:不保证插入顺序,但是循环遍历时,输出顺序是不会改变的。)
Map接口:双列数据,保存具有映射关系“key-value对”的,
Map 中的 key 用Set来存放,不允许重复,同时set是元素无序、不可重复的集合,因此map,也是根据key无序、不可重复的集合
三、集合的简单使用
下面我们通过ArrayList这个最典型的集合对集合的使用进行介绍
package study01;
import java.util.*;
public class test2 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
// 增加
arrayList.add("aaa ");
arrayList.add("bbb ");
arrayList.add("ccc ");
arrayList.add("ddd ");
arrayList.add("ddd ");
arrayList.add("ddd ");
// 删除
arrayList.remove("ddd ");
// 输出
System.out.println(arrayList.get(0));
System.out.println(arrayList);
// 迭代器操作
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.print(next);
if (next.equals("ccc ")) {
// 调用迭代器的remove方法
iterator.remove();
}
}
System.out.print("\n"+arrayList);
}
}
四、集合中的排序sort()
要想对集合进行排序sort,集合中的元素必须是Comparable
(ctrl + p查看方法参数)
首先我们知道sort()是一个作用于排序的方法,但不是万事万物都可以排序的(如果你想让一群自定义Dog() 对象排序,他们该以什么方式进行排序呢?),所以这需要程序员们自行定义排序的方式。
而要想让集合可以实现sort() 方法则集合中的对象必须是Comparable,因此必须满足以下条件的其中之一:
集合中的元素必须是实现Comparable接口的类型。
使用重载compare() 方法,取用Comparator参数的构造函数来实现排序。
下面我们选用第二种方式,因为这种方式更加灵活:
我们首先创建一个class Dog,同时创建一个内部类实现 Comparator 接口重载compare方法。再通过main方法调用sort进行测试。
package study01;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Objects;
public class Dog {
ArrayList<Dog> arrayList = new ArrayList<>();
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
Dog(String name, Integer age){
this.name = name;
this.age = age;
}
// 声明一个静态内部类IdCompare 实现 Comparator 接口 用于通过age对Dog进行排序
static class AgeCompare implements Comparator<Dog> {
// 覆盖compare()方法,通过age排序
@Override
public int compare(Dog o1, Dog o2) {
return o1.getAge().compareTo(o2.getAge());
}
}
// 声明一个静态内部类NameCompare 实现 Comparator 接口
static class NameCompare implements Comparator<Dog> {
// 覆盖compare()方法,通过Name排序
@Override
public int compare(Dog o1, Dog o2) {
return o1.getName().compareTo(o2.getName());
}
}
public static void main(String[] args) {
ArrayList<Dog> arrayList = new ArrayList<>();
arrayList.add(new Dog("ccc ", 2));
arrayList.add(new Dog("ddd ", 7));
arrayList.add(new Dog("aaa ", 11));
arrayList.add(new Dog("bbb ", 6));
arrayList.add(new Dog("bbb ", 6));
// 按插入顺序输出
for (Dog a:arrayList){
System.out.print(a.name+a.age +"\t");
}
System.out.println();
// 通过 age 排序
arrayList.sort(new AgeCompare());
for (Dog a:arrayList){
System.out.print(a.name+a.age +"\t");
}
System.out.println();
// 通过 name 排序
arrayList.sort(new NameCompare());
for (Dog a:arrayList){
System.out.print(a.name+a.age +"\t");
}
System.out.println();
}
}
五、contains()
contains(a)方法用于判断集合中是否包含元素a。
要想对对象调用contains()方法就要重写equals和hashCode。因为contains()底层也通过调用equals() 和 hashCode() 判断。
1. equals()
要判断集合中是否包含就要比较集合中是否有元素相等。那么两个对象要怎样才算相等呢?
a. 引用性相等:堆上同一对象的两个引用相等
b. 对象相等性:堆上两个不同对象在意义上是相同的
因此这里我们要先介绍以下运算符 “==” 和 方法 equals()
“==”:
①如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
②如果比较的是引用数据类型变量:比较两个对象的地址值是否相同。(即两个引用是否指向同一实体)
equals() :
①只能适用于引用数据类型
②Object类中定义的equals()和==的作用是相同的。
Object类(Java中的根父类)中equals()的定义(也是通过“==”实现):
③通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的成员变量是否相同。那么,我们就需要对Object类中的equals()进行重写。
因此对于引用性相等我们可以用“==”判断,而对于”复杂对象在意义上相同“的判定我们可以通过重写equals()方法来实现
class Dog 重写Object 中的equals()方法:
@Override
public boolean equals(Object o) {
// 如果引用的地址相同,那么内容肯定也相同,返回true
if (this == o) return true;
// 如果数据类型不同,那么对象肯定也不同,返回false
if (!(o instanceof Dog)) return false;
Dog user = (Dog) o;
// 上面几行的判断只是为了提高判断效率,下面这一行这才是关键代码,不过也只是依次判断对象中各成员变量是否相同罢了
return Objects.equals(arrayList, user.arrayList) && Objects.equals(getName(), user.getName()) && Objects.equals(getAge(), user.getAge());
}
在main方法下测试一下:
System.out.println(arrayList.contains(new Dog("aaa ", 11)));
这里会返回true, 如果把重写的equals()方法注释掉,那么contains判断的标准变成Object 中equals()方法的引用性相等,会返回false,感兴趣的小伙伴可以试试。
2. hashCode()
hashCode()方法的存在是用来缩小寻找成本的:如果集合在对比的时候发现同样的hashcode有多个对象,那么它会使用equals() 来判断是否有完全符合。
有 a.equals(b) 时,必有 a.hashCode() == b.hashCode()
有 a.hashCode() == b.hashCode()时, 不一定有 a.equals(b)
@Override
public int hashCode() {
return Objects.hash(arrayList, getName(), getAge());
}
六、举一反三
1、要想包含Dog()对象的集合可以实现sort()方法就要让Dog类重载compare() 方法
2、要想包含Dog()对象的集合可以实现contains()方法就要让Dog类重载equals和hashCode
3、而HashSet作为一个无序不重复的集合,也就意味着它集合中的对象需要通过重载equals和hashCode方法来判断是否重复,不重载equals和hashCode的话打印出来的集合还是会重复(因为你没有制定判断重复的标准),不信你试试。
HashSet<Dog> hashSet = new HashSet<>(arrayList); System.out.print("hashSet:\t"); for (Dog a:hashSet) { System.out.print(a.getName()+a.getAge()+"\t"); } System.out.println();
4、最后讲一下map
Map 中的 key 用Set来存放,key值不允许重复,因此同hashset同理,同一个 Map 对象所对应 的类,须重写hashCode()和equals()方法
七、最后附上完整的测试代码:
package study01;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Objects;
public class Dog {
ArrayList<Dog> arrayList = new ArrayList<>();
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
Dog(String name, Integer age){
this.name = name;
this.age = age;
}
// 声明一个静态内部类IdCompare 实现 Comparator 接口
static class AgeCompare implements Comparator<Dog> {
// 覆盖compare()方法,通过age排序
@Override
public int compare(Dog o1, Dog o2) {
return o1.getAge().compareTo(o2.getAge());
}
}
// 声明一个静态内部类NameCompare 实现 Comparator 接口
static class NameCompare implements Comparator<Dog> {
// 覆盖compare()方法,通过Name排序
@Override
public int compare(Dog o1, Dog o2) {
return o1.getName().compareTo(o2.getName());
}
}
@Override
public boolean equals(Object o) {
// 如果引用的地址相同,那么内容肯定也相同,返回true
if (this == o) return true;
// 如果数据类型不同,那么对象肯定也不同,返回false
if (!(o instanceof Dog)) return false;
Dog user = (Dog) o;
// 上面几行的判断只是为了提高判断效率,下面这一行这才是关键代码,不过也只是依次判断对象中各成员变量是否相同罢了
return Objects.equals(arrayList, user.arrayList) && Objects.equals(getName(), user.getName()) && Objects.equals(getAge(), user.getAge());
}
@Override
public int hashCode() {
return Objects.hash(arrayList, getName(), getAge());
}
public static void main(String[] args) {
ArrayList<Dog> arrayList = new ArrayList<>();
arrayList.add(new Dog("ccc ", 2));
arrayList.add(new Dog("ddd ", 7));
arrayList.add(new Dog("aaa ", 11));
arrayList.add(new Dog("bbb ", 6));
arrayList.add(new Dog("bbb ", 6));
// 按插入顺序输出
for (Dog a:arrayList){
System.out.print(a.name+a.age +"\t");
}
System.out.println();
// 通过 age 排序
arrayList.sort(new AgeCompare());
for (Dog a:arrayList){
System.out.print(a.name+a.age +"\t");
}
System.out.println();
// 通过 name 排序
arrayList.sort(new NameCompare());
for (Dog a:arrayList){
System.out.print(a.name+a.age +"\t");
}
System.out.println();
// 判断是否包含
System.out.println(arrayList.contains(new Dog("aaa ", 11)));
// hashSet 无序不重复的集合
HashSet<Dog> hashSet = new HashSet<>(arrayList);
for (Dog a:hashSet) {
System.out.print(a.getName()+a.getAge()+"\t");
}
System.out.println();
}
}