文章目录
我们很容易理解整型的 i>j 这样的比较方式,但当我们对多个对象进行排序时,如何比较两个对象的“大小”呢?这样的比较 stu1 > stu2 显然是不可能通过编译的。
为了解决如何比较两个对象大小的问题,JDK提供了两个接口 java.lang.Comparable 和 java.util.Comparator 。
Comparable
Comparable接口
所有可以 “排序” 的类都实现了java.lang.Comparable接口,Comparable接口中只有一个方法。Comparable接口中只有一个方法:实现了 Comparable 接口的类通过实现 comparaTo 方法从而确定该类对象的排序方式。
public int compareTo(T o);
返回 0 表示 this == obj
返回整数表示 this > obj
返回负数表示 this < obj
调用此方法的对象,也就是this和o进行比较,若返回值大于0则this大于o,返回值等于0则是this等于o,返回值小于0则是this<o,而这个Comparable是直接在我们的自定义类User上实现,因为this是需要一个明确的比较对象的,也就是一般情况下我们会在定义User类的时候有排序的需求,就要实现此接口。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UserComparable implements Comparable<UserComparable> {
private String name;
private int age;
public UserComparable(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "UserComparable{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(UserComparable o) {
//由于字符串无法直接比较大小,所以调用String类的compareTo
if (this.name.compareTo(o.name)==0){
if (this.age == o.age){
return 0;
}else if (this.age >o.age){
return 1;
}else {
return -1;
}
}else if (this.name.compareTo(o.name)>0){
return 1;
}else {
return -1;
}
}
public static void main(String[] args) {
List<UserComparable> list = new ArrayList<UserComparable>();
list.add(new UserComparable("gol",21));
list.add(new UserComparable("gol",19));
list.add(new UserComparable("xiao",21));
list.add(new UserComparable("long",21));
System.out.println("排序前:"+list);
//排序规则:先按name排序,若name相等则再比较age
Collections.sort(list);
System.out.println("排序后:"+list);
}
}
Comparator:
Comparator接口中方法很多,但是我们只需要实现一个,也是最重要的一个compare,也许有的人会好奇为什么接口中的方法可以不用实现,因为这是JDK8以后的新特性,在接口中用default修饰的方法可以有方法体,在实现接口的时候可以不用重写,可以类比抽象类。
int compare(T o1, T o2);
compare比较的o1和o2,返回值大于0则o1大于o2,依次类推,对于compare来说this是谁不重要,所比较的两个对象都已经传入到方法中,所以Comparator就是个外部比较器,在我们设计User初时,并不需要它有比较功能,在后期扩展业务是,Comparator的存在可以使我们在不修改源代码的情况下来完成需求,只需要新定义一个比较器来实现Comparator,重写compare方法并将User对象传进去。
public class emp {
public int age;
public String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public emp(int age, String name) {
super();
this.age = age;
this.name = name;
}
public emp() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "emp [age=" + age + ", name=" + name + "]";
}
}
import org.junit.Test;
import junit.framework.TestCase;
public class TestCompare extends TestCase {
@Test
public void test2(){
List<emp> list=new ArrayList<emp>();
emp test1=new emp(69,"李四");
emp test2=new emp(29,"王五");
emp test3=new emp(28,"赵六");
emp test4=new emp(20,"钱三");
list.add(test4);
list.add(test3);
list.add(test2);
list.add(test1);
Collections.sort(list,new Comparator<emp>(){
@Override
public int compare(emp o1, emp o2) {
if(o1.age==o2.age&&o1.name==o2.name){
return 0;
}else if(o1.age>o2.age){
return 1;
}else{
return 0;
}
}
});
for(Object s:list){
System.out.println(s);
}
}
}
结论:
1、Comparator 使用比较灵活,不需要修改实体类源码,但是需要实现一个比较器。
2、Comparable 使用简单,但是对代码有侵入性,需要修改实体类源码。
3、java中大部分我们常用的数据类型的类都实现了Comparable接口,而仅仅只有一个抽象类RuleBasedCollator实现了Comparator接口 ,还是我们不常用的类,
这并不是说要用Comparab而不要使用Comparator,在设计初时有需求就选择Comparable,若后期需要扩展或增加排序需求是,再增加一个比较器Comparator,毕竟能写Collections.sort(arg1),没人乐意写Collections.sort(arg1,arg2)。
策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
介绍
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
实现
我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。
StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。
步骤 1
创建一个接口。
步骤 2
创建实现接口的实体类。
步骤 3
创建 Context 类。
步骤 4
使用 Context 来查看当它改变策略 Strategy 时的行为变化。
步骤 5
执行程序,输出结果: