先入为主,应用实例如下:
package cn.edu.ujn.offersword;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class C5_33_SortArrayForMinNumber {
/**
* @date 2016-09-15
* @number 04
* @author SHQ
* 把数组排成最小的数
* 题目描述
* 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
* 例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
* 思路
* 通过Collection.sort()方法中的自定义比较器实现。若mn>nm,则说明nm满足要求。
* 时间复杂度 O(nlogn);
* 空间复杂度O(2);
*/
public static void main(String[] args) {
int[] numbers = {3,5,1,4,2};
System.out.println(PrintMinNumber(numbers));
}
private static String PrintMinNumber(int [] numbers) {
if(numbers == null || numbers.length == 0){
return null;
}
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i = 0; i < numbers.length; i++){
list.add(numbers[i]);
}
// 升序(自定义比较器)
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer str1, Integer str2) {// 实现接口中的方法
return (str1+""+str2).compareTo(str2+""+str1);
}
});
StringBuffer sb = new StringBuffer();
for(int str : list){
sb.append(str);
}
return sb.toString();
}
}
注 sort讲解
翻翻 API 会发现, Arrays.sort
还有种重载形式:sort(T[] a, Comparator<? super T> c)
,这个方法参数的写法用到了泛型,我们还没讲到。我们可以把它理解成这样的形式: sort(Object[] a, Comparator c)
,这个方法的意思是按照比较器 c 给出的比较排序算法,对 Object 数组进行排序。Comparator
接口中定义了两个方法: compare(Object o1, Object o2)
和 equals
方法,由于 equals
方法所有对象都有的方法,因此当我们实现 Comparator
接口时,我们只需重写 compare
方法,而不需重写 equals
方法。Comparator
接口中对重写 equals
方法的描述是:“注意,不重写 Object.equals(Object)
方法总是安全的。然而,在某些情况下,重写此方法可以允许程序确定两个不同的 Comparator 是否强行实施了相同的排序,从而提高性能。”。我们只需知道第一句话就OK了,也就是说,可以不用去想应该怎么实现 equals
方法,因为即使我们不显示实现 equals
方法,而是使用Object
类的 equals
方法,代码依然是安全的。
排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n log(n)
性能。指定列表必须是可修改的,但不必是可大小调整的。此实现将指定列表转储到一个数组中,并对数组进行排序,在重置数组中相应位置每个元素的列表上进行迭代。这避免了由于试图原地对链接列表进行排序而产生的 n2 log(n)
性能。
参数:
list
- 要排序的列表。
c
- 确定列表顺序的比较器。null 值指示应该使用元素的自然顺序。
抛出:
ClassCastException
- 如果列表中包含不可使用指定比较器相互比较 的元素。
UnsupportedOperationException
- 如果指定列表的列表迭代器不支持 set 操作。
拓展阅读 java 自动装箱与拆箱
这个是jdk1.5以后才引入的新的内容。java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装称为装箱,解包装称为拆箱);
其实按照我自己的理解自动装箱就可以简单的理解为将基本数据类型封装为对象类型,来符合java的面向对象;例如用int来举例:
//声明一个Integer对象
Integer num = 10;
//以上的声明就是用到了自动装箱:解析为
Integer num = new Integer(10);
以上就是一个很好的体现,因为10是属于基本数据类型的,原则上它是不能直接赋值给一个对象Integer的,但jdk1.5后你就可以进行这样的声明,这就是自动装箱的魅力。
自动将基本数据类型转化为对应的封装类型。成为一个对象以后就可以调用对象所声明的所有的方法。
自动拆箱:故名思议就是将对象重新转化为基本数据类型:
//装箱
Integer num = 10;
//拆箱
int num1 = num;
自动拆箱有个很典型的用法就是在进行运算的时候:因为对象是不能直接进行运算的,而是要转化为基本数据类型后才能进行加减乘除。
Integer num = 10;
//进行计算时隐含的有自动拆箱
System.out.print(num--);
看下面一个例子,
//在-128~127 之外的数
Integer num1 = 297; Integer num2 = 297;
System.out.println("num1==num2: "+(num1==num2));
// 在-128~127 之内的数
Integer num3 = 97; Integer num4 = 97;
System.out.println("num3==num4: "+(num3==num4));
打印的结果是:num1==num2: false num3==num4: true
很奇怪吧:这就归结于java对于Integer与int的自动装箱与拆箱的设计,是一种模式:享元模式(flyweight)
为了加大对简单数字的重利用,java定义:在自动装箱时对于值从–128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用,始终只存在一个对象。
而如果超过了从–128到127之间的值,被装箱后的Integer对象并不会被重用,即相当于每次装箱时都新建一个 Integer对象;明白了吧。
以上的现象是由于使用了自动装箱所引起的,如果你没有使用自动装箱,而是跟一般类一样,用new来进行实例化,就会每次new就都一个新的对象;
这样的自动装箱拆箱不仅在基本数据类型中有应用,在String类中也有应用,比如我们经常声明一个String对象时:
String str = "sl";
//代替下面的声明方式
String str = new String("sl");