Loading

Compator 和 Comparable 以及策略模式

Comparable

Comparable 接口

废话不说,直接上源码,并且截取保留了一部分注释的翻译

package java.lang;
import java.util.*;

/**
 * 该接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法 。
 * 
 *
 * Collections.sort (和Arrays.sort )可以自动对实现此接口的对象进行列表(和数组)排序。
 * 实现该接口的对象,可以使用如在键sorted map或作为在元件sorted set ,而不需要指定一个comparator 。 
 *
 * 一类C的自然顺序被说成是与equals一致当且仅当e1.compareTo(e2) == 0对每一个e1和C类e2相同的布尔值e1.equals(e2)。
 * 请注意, null不是任何类的实例, e.compareTo(null)应该抛出一个NullPointerException即使e.equals(null)返回false 。 
 * 
 * @param <T> the type of objects that this object may be compared to
 *
 * @author  Josh Bloch
 * @see java.util.Comparator
 * @since 1.2
 */
public interface Comparable<T> {
    
    public int compareTo(T o);
}

重写的 compareTo 方法必须要满足类似于 equals 方法的要求(以下是 equals 方法的要求):

1、自反性:对于任何非空引用x,x.equals(x)应该返回true。

2、对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。

3、传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。

4、一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。

5、非空性:对于任意非空引用x,x.equals(null)应该返回false。

对应 compareTo 的规则是:

1、将此对象与指定的对象进行比较以进行排序。 返回一个负整数,零或正整数,对应该对象小于,等于或大于指定对象。

2、对称性、非空性:实现程序必须确保sgn(x.compareTo(y)) == -sgn(y.compareTo(x))所有xy。(这意味着x.compareTo(y)必须抛出异常 y.compareTo(x)引发异常。)

3、传递性:(x.compareTo(y)>0 && y.compareTo(z)>0)表示x.compareTo(z)>0

简而言之,若一个类实现了 comparable 接口,则意味着该类支持排序。

Integer 实现

public final class Integer extends Number implements Comparable<Integer> {

    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }

    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
}

String 实现

public int compareTo(String anotherString) {
    int len1 = value.length;
    int len2 = anotherString.value.length;
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;

    int k = 0;
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    return len1 - len2;
}

Comparator

// A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort
// method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order. Comparators can also be used
// to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for 
// collections of objects that don't have a natural ordering.
@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);

    // 还有一些默认的比较器实现

    // comparingInt 实现
    public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
    }
}

// ToIntFunction 接口
@FunctionalInterface
public interface ToIntFunction<T> {

    /**
     * Applies this function to the given argument.
     *
     * @param value the function argument
     * @return the function result
     */
    int applyAsInt(T value);
}

我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么,我们可以新建一个该类的比较器来进行排序。这个比较器只需要实现 Comparator 即可。

Collections.sort

对于以下实体类,我们用两种方式进行排序

class UserInfo {
	public int userid;
	public int age;
	public String name;
	public UserInfo(int userid, int age, String name) {
		this.userid = userid;
		this.age = age;
		this.name = name;
	}
}

实现 Comparable 接口

实现 Comparable 接口之后,UserInfo 类就可以像 Integer 那样自然排序了。

class UserInfo implements Comparable<UserInfo> {
	public int userid;
	public int age;
	public String name;
	public UserInfo(int userid, int age, String name) {
		this.userid = userid;
		this.age = age;
		this.name = name;
	}

	@Override
	public int compareTo(UserInfo o) {
		if (this.age - o.age == 0) {
			// 二者年龄一样大
			// 根据userID 比较
			return this.userid - o.userid;
		}
		return this.age - o.age;
	}
}

使用 Comparator

显然,我们使用 Comparator 来对没有实现 Comparable 接口的对象进行排序更为简便

Collections.sort(list, ((o1, o2) -> {
    if (o1.age == o2.age) {
        return o1.userid - o2.userid;
    }
    return o1.age - o2.age;
}));

策略模式

Collection.sort 的策略模式

Comparator 体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

让我们看一下 Collections.sort 源码来体会一下这种思想吧:

// 1
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

// 2 List 接口的实现
default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);	// 3
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {	// 重新设置数组内容,this 指代 list
        i.next();
        i.set((E) e);
    }
}

// 3 Arrays 类
public static <T> void sort(T[] a, Comparator<? super T> c) {
    if (c == null) {
        sort(a);	// 4
    } else {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a, c);
        else
            TimSort.sort(a, 0, a.length, c, null, 0, 0);	//4 或者这里
    }
}

// 4
static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) {
    assert a != null && lo >= 0 && lo <= hi && hi <= a.length;

    int nRemaining  = hi - lo;
    if (nRemaining < 2)
        return;

    if (nRemaining < MIN_MERGE) {
        int initRunLen = countRunAndMakeAscending(a, lo, hi);	// 6
        binarySort(a, lo, hi, lo + initRunLen);
        return;
        ...
    }
}

// 6
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
    assert lo < hi;
    int runHi = lo + 1;
    if (runHi == hi)
        return 1;

    // Find end of run, and reverse range if descending
    if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending
        while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)	// 调用了 Compator 的 compare 方法
            runHi++;
        reverseRange(a, lo, runHi);
    } else {                              // Ascending
        while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)
            runHi++;
    }

    return runHi - lo;
}

可以看到,使用 Collections.sort 对 List 进行排序的话会调用 List.sort 方法,并且将数据字段转储到数组中进行排序,进行排序最终也还是调用了 Compator 的 compare 方法。

策略模式的结构与实现

策略模式的结构

  1. 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  2. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
  3. 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

实现

image

策略模式应用场景

  • 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
  • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
  • 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
  • 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
  • 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

参考文章:https://segmentfault.com/a/1190000040419861

posted @ 2022-05-03 22:12  槐下  阅读(172)  评论(0编辑  收藏  举报