Java提高十五:容器元素比较Comparable&Comparator深入分析
我们经常用容器来存放元素,通常而言我们是不关系容器中的元素是否有序,但有些场景可能要求容器中的元素是有序的,这个时候用ArrayList LinkedList Hashtable HashMap HashSet 这些容器本身存放的时候就没有办法做到了,这个时候我们有两种思路:第一种思路:对刚刚我们提到的容器类的元素从新排序后存放,就是后面我们要介绍的利用Collections.sort 方法进行排序,第二种思路:是容器在添加元素的时候就进行大小的比较从而来保证元素的排序。下面我开始来详细介绍本节内容。
一、排序概念
在讲解上面的知识点前,我们首先需要知道最基础的知识,即什么是排序?
排序:将一组数据按照某种规则进行排列顺序。
1、规则:
- 基本数据类型:如数据,就是日常的大小顺序
- 引用数据类型:
- 内置类(String,Integer等):内部已经指定好规则,直接使用即可。
- 自定义的类:需要按业务规则排序。
2、顺序:
- 升序:从小到大
- 降序:从大到小
3、排列:算法,如:冒泡、选择、插入、shell、堆排序等等
二、冒泡排序
上面说到排序的时候算法有很多种,那么这里我们介绍最简单的一种,即:冒泡排序。
我们将会三个版本来进行演练:
- 简易版:简单
- 优化版:减少每趟次数
- 最终版:考虑有序,减少趟数
简易版:
import java.util.Arrays; public class BubbleSort1 { /**
* @param args
*/
public static void main(String[] args) {
int[] arr ={9,8,7,6,5};
sort(arr); }
//第一版本,很简单
public static void sort(int[] arr){
int len =arr.length;
for(int j=0;j<len-1;j++){
System.out.println("第"+(j+1)+"趟");
for(int i=0;i<len-1;i++){
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
}
}
}
public static void sortSecond(int[] arr){
System.out.println("第一趟");
for(int i=0;i<arr.length-1;i++){
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
}
System.out.println("第二趟");
for(int i=0;i<arr.length-1;i++){
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
}
System.out.println("第三趟");
for(int i=0;i<arr.length-1;i++){
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
}
System.out.println("第四趟");
for(int i=0;i<arr.length-1;i++){
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
} } public static void sortFirst(int[] arr){
System.out.println("第一趟");
for(int i=0;i<arr.length-1;i++){
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
} /*
//第一趟 第一次
System.out.println("第一趟 第一次");
int i=0;
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
System.out.println("第一趟 第二次");
i++;
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr)); System.out.println("第一趟 第三次");
i++;
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr)); System.out.println("第一趟 第四次");
i++;
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr)); */
} }
说明:即每个元素需要和后面的元素比较arr.length -1 次;那么arr.length 个元素,需要进行arr.length -1 趟比较,于是就有了上面的代码。
优化版本:
package com.bjsxt.sort.bubble; import java.util.Arrays; public class BubbleSort2 { /**
* @param args
*/
public static void main(String[] args) {
int[] arr ={9,8,7,6,5};
sort(arr);
}
//第二版本,减少每一趟的次数
public static void sort(int[] arr){
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
System.out.println("第"+(j+1)+"趟");
for(int i=0;i<len-1-j;i++){ //次数
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
}
}
} }
说明:即在每一趟元素比较的时候,其和每个元素比较的次数应该是不需要去重复进行前面比较过的,因此需要减少每趟比较的次数,即:地刺循环的时候变成了arr.length-1-j.
最终版本:
package com.bjsxt.sort.bubble; import java.util.Arrays; /**
* 最终版本:考虑存在顺序
* @author Administrator
*
*/
public class BubbleSort { /**
* @param args
*/
public static void main(String[] args) {
int[] arr ={1,2,9,3,4};
sort1(arr); System.out.println("==========final============");
arr =new int[]{9,1,2,3,4};
sortFinal(arr);
}
//第二版本,减少每一趟的次数
public static void sortFinal(int[] arr){
boolean sorted= true;
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
sorted =true; //假定有序
for(int i=0;i<len-1-j;i++){ //次数
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
sorted =false; //假定失败
}
System.out.println(Arrays.toString(arr));
}
if(sorted){ //减少趟数
break;
}
}
} //第二版本,减少每一趟的次数
public static void sort1(int[] arr){
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
System.out.println("第"+(j+1)+"趟");
for(int i=0;i<len-1-j;i++){ //次数
System.out.print("第"+(i+1)+"次");
if(arr[i]>arr[i+1]){
int temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
}
System.out.println(Arrays.toString(arr));
}
}
} }
说明:即如果有序了,则不需要进行比较了。
三、TreeSet 和 TreeMap
在讲解TreeSet 和 TreeMap 前,我们要先介绍Comparable接口:
- “排序”的实体类都实现了java.lang.Comparable 接口,Comparable 接口中只有一个方法,即:public int compareTo(T o); 0 相等 正数 负数
- 实现了Comparable接口的类通过实现了的compareTo 方法从而确定该类对象的排序方式。
1、内置类:
Integer:
public final class Integer extends Number implements Comparable<Integer> {
//...... public int compareTo(Integer anotherInteger) {
int thisVal = this.value;
int anotherVal = anotherInteger.value;
return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
} }
Character:
public final
class Character extends Object implements java.io.Serializable, Comparable<Character> { public int compareTo(Character anotherCharacter) {
return this.value - anotherCharacter.value;
} }
String:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence{ public int compareTo(String anotherString) {
int len1 = count;
int len2 = anotherString.count;
int n = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset; if (i == j) {
int k = i;
int lim = n + i;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
} else {
while (n-- != 0) {
char c1 = v1[i++];
char c2 = v2[j++];
if (c1 != c2) {
return c1 - c2;
}
}
}
return len1 - len2;
} }
Date;
public class Date
implements java.io.Serializable, Cloneable, Comparable<Date>
{
public int compareTo(Date anotherDate) {
long thisTime = getMillisOf(this);
long anotherTime = getMillisOf(anotherDate);
return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
}
}
总结:
/**
* 内置引用数据类型(常用)的比较
* @author Administrator
*
*/
public class Demo01 { /**
* @param args
*/
public static void main(String[] args) {
Integer a ; //根据基本数据类型大小
Character ch; //根据Unicode编码顺序
String str="abc"; //如果其中一个是例外一个起始开始的子串,返回长度之差
String str2 ="abcd123"; //否则返回第一个不相等的unicode码之差
System.out.println(str.compareTo(str2));
str ="abc";
str2 ="aad";
System.out.println(str.compareTo(str2)); java.util.Date d ; //根据日期的长整形数比较
} }
2、内置类集合数组排序
String:
import java.util.Arrays; public class Demo02 { /**
* @param args
*/
public static void main(String[] args) {
String[] arr ={"a","abcd","abc","def"};
//从到小排序 降序
boolean sorted= true;
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
sorted =true; //假定有序
for(int i=0;i<len-1-j;i++){ //次数
if(((Comparable)arr[i]).compareTo(arr[i+1])<0){
String temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
sorted =false; //假定失败
}
}
if(sorted){ //减少趟数
break;
}
} System.out.println(Arrays.toString(arr));
} }
Date:
import java.util.Arrays;
import java.util.Date; public class Demo03 { /**
* @param args
*/
public static void main(String[] args) {
Date[] arr =new Date[3];
arr[0] =new Date();
arr[1] =new Date(System.currentTimeMillis()-1000*60*60);
arr[2] =new Date(System.currentTimeMillis()+1000*60*60);
//降序 //从大到小排序 降序
boolean sorted= true;
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
sorted =true; //假定有序
for(int i=0;i<len-1-j;i++){ //次数
if(((Comparable)arr[i]).compareTo(arr[i+1])<0){
Date temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
sorted =false; //假定失败
}
}
if(sorted){ //减少趟数
break;
}
} System.out.println(Arrays.toString(arr));
} }
可以看到我们可以抽出工具类来,因为大部分代码是一样的,抽出工具类,则需要考虑可以放多种数据类型,因此会考虑到泛型和Object[] 来存放,工具类如下:
import java.util.Comparator;
import java.util.List; /**
* 排序
* @author Administrator
*
*/
public class Utils {
/**
* List的排序+比较器
* @param list
* @param com
*/
public static <T> void sort(List<T> list,Comparator<T> com){
//第一步:转成数组
Object[] arr =list.toArray();
sort(arr,com);
//第二步:改变容器中对应的值
for(int i=0;i<arr.length;i++){
list.set(i, (T)(arr[i]));
}
} /**
* 数组的排序 (降序)+Comparator接口
* @param arr
*/
public static <T> void sort(Object[] arr,Comparator<T> com){
//从大到小排序 降序
boolean sorted= true;
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
sorted =true; //假定有序
for(int i=0;i<len-1-j;i++){ //次数
if(com.compare((T)arr[i], (T)arr[i+1])<0){
Object temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
sorted =false; //假定失败
}
}
if(sorted){ //减少趟数
break;
}
}
} /**
* 容器排序 (使用泛型方法)
*/
public static <T extends Comparable<T>> void sort(List<T> list){
//第一步:转成数组
Object[] arr =list.toArray();
sort(arr);
//第二步:改变容器中对应的值
for(int i=0;i<arr.length;i++){
list.set(i, (T)(arr[i]));
} } /**
* 数组排序 (使用泛型方法)
*/
public static <T extends Comparable<T>> void sort(T[] arr){
//从大到小排序 降序
boolean sorted= true;
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
sorted =true; //假定有序
for(int i=0;i<len-1-j;i++){ //次数
if(((Comparable)arr[i]).compareTo(arr[i+1])<0){
T temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
sorted =false; //假定失败
}
}
if(sorted){ //减少趟数
break;
}
}
} /**
* 数组的排序 (降序)
* @param arr
*/
public static void sort(Object[] arr){
//从大到小排序 降序
boolean sorted= true;
int len =arr.length;
for(int j=0;j<len-1;j++){ //趟数
sorted =true; //假定有序
for(int i=0;i<len-1-j;i++){ //次数
if(((Comparable)arr[i]).compareTo(arr[i+1])<0){
Object temp = arr[i];
arr[i] =arr[i+1];
arr[i+1] =temp;
sorted =false; //假定失败
}
}
if(sorted){ //减少趟数
break;
}
} } }
然后接下来,我们使用我们的工具类进行排序操作:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List; public class Demo04 { /**
* @param args
*/
public static void main(String[] args) {
Date[] arr =new Date[3];
arr[0] =new Date();
arr[1] =new Date(System.currentTimeMillis()-1000*60*60);
arr[2] =new Date(System.currentTimeMillis()+1000*60*60);
Utils.sort(arr); //降序
System.out.println(Arrays.toString(arr)); //字符串
String[] arr2 ={"a","abcd","abc","def"};
Utils.sort(arr2);
System.out.println(Arrays.toString(arr2)); System.out.println("==========List排序===========");
//存放容器中
List<String> list =new ArrayList<String>();
list.add("a");
list.add("abcd");
list.add("abc");
list.add("def");
Utils.sort(list);
System.out.println(list); System.out.println("==========使用Comparator 排序数组===============");
arr2 =new String[]{"a","abcd","abc","def"};
Utils.sort(arr2,new StringComp());
System.out.println(Arrays.toString(arr2)); System.out.println("==========List排序+比较器===========");
list =new ArrayList<String>();
list.add("a");
list.add("abcd");
list.add("abc");
list.add("def");
Utils.sort(list,new StringComp());
System.out.println(list); } }
/**
* 排序规则的业务类
* @author Administrator
*
*/
public class StringComp implements java.util.Comparator<String>{ /**
* 按长度比较大小
* 正数 >
* 负数 <
* 0 ==
*/
@Override
public int compare(String o1, String o2) {
int len1 =o1.length();
int len2 =o2.length();
return -(len1-len2);
} }
上面我们使用的是自己定义的工具类,下面我们使用JDK 提供的工具类进行排序操作:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; /**
* 使用Collections对容器的比较
* 1、 public static <T> void sort(List<T> list, Comparator<? super T> c)
* 2、public static <T extends Comparable<? super T>> void sort(List<T> list)
* void sort(List<T> list)
* @author Administrator
*
*/
public class Demo05 { /**
* @param args
*/
public static void main(String[] args) {
List<String> list =new ArrayList<String>();
list.add("a");
list.add("abcd");
list.add("abc");
list.add("def");
Collections.sort(list,new StringComp());
System.out.println(list); list =new ArrayList<String>();
list.add("a");
list.add("abcd");
list.add("abc");
list.add("def");
Collections.sort(list);
System.out.println(list); } }
3、自定义类数据类型的排序
对于自定义的数据类型排序,两种方法:1.实体类 实现java.lang.Comparable + compareTo 2.业务排序类 实现java.util.Comparator + compare 方法
第二种方法,可以达到解耦的目的,比如实体类是一个calss 文件或者是其它部件提供的实体类等,可以通过定义业务排序类达到排序,更加灵活。
实例一:Comparable 实现排序
import java.text.SimpleDateFormat;
import java.util.Date; /**
* 新闻条目实体类
* @author Administrator
*
*/
public class NewsItem implements java.lang.Comparable<NewsItem>{
//标题
private String title;
//点击量
private int hits;
//时间
private Date pubTime;
public NewsItem() {
} public NewsItem(String title, int hits, Date pubTime) {
super();
this.title = title;
this.hits = hits;
this.pubTime = pubTime;
} public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getHits() {
return hits;
}
public void setHits(int hits) {
this.hits = hits;
}
public Date getPubTime() {
return pubTime;
}
public void setPubTime(Date pubTime) {
this.pubTime = pubTime;
} //时间降序 +点击量升序+标题降序
@Override
public int compareTo(NewsItem o) {
int result =0;
//比较 时间
result =-this.pubTime.compareTo(o.pubTime); //降序
if(0==result){ //时间相同
//点击量
result =this.hits-o.hits; //升序
if(0==result){ //点击量相同
//标题
result=-this.title.compareTo(o.title);//降序
}
} return result;
} @Override
public String toString() {
StringBuilder sb =new StringBuilder();
sb.append("标题:").append(this.title);
sb.append(",时间:").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.pubTime));
sb.append(",点击量:").append(this.hits).append("\n");
return sb.toString();
} }
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List; import com.bjsxt.sort.innerType.Utils; /**
* 使用Collections
* @author Administrator
*
*/
public class NewsItemApp { /**
* @param args
*/
public static void main(String[] args) {
List<NewsItem> news=new ArrayList<NewsItem>();
news.add(new NewsItem("美国后怕了,逃跑了悲剧了",50,new Date(System.currentTimeMillis()-1000*60*60)));
news.add(new NewsItem("中国登上钓鱼岛了,全国欢呼了",100,new Date()));
news.add(new NewsItem("小日本终于听话了,泪流满面笑了",60,new Date(System.currentTimeMillis()-1000*60*60)));
System.out.println("排序前:"+news);
//排序
//Collections.sort(news);
Utils.sort(news);
System.out.println("排序后"+news); } }
实例二:Comparator 实现排序
/**
* 实体类
* @author Administrator
*
*/
public class Goods {
//商品名称
private String name;
//价格
private double price;
//收藏量
private int fav;
public Goods() {
// TODO Auto-generated constructor stub
} public Goods(String name, double price, int fav) {
super();
this.name = name;
this.price = price;
this.fav = fav;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getFav() {
return fav;
}
public void setFav(int fav) {
this.fav = fav;
} @Override
public String toString() {
return "商品名:"+name+",收藏量"+this.fav+",价格:"+this.price+"\n";
}
}
/**
* 按收藏量排序的业务类 (升序)
* @author Administrator
*
*/
public class GoodsFavComp implements java.util.Comparator<Goods> { @Override
public int compare(Goods o1, Goods o2) {
return o1.getFav()-o2.getFav();
} }
/**
* 按价格排序的业务类 (降序)
* @author Administrator
*
*/
public class GoodsPriceComp implements java.util.Comparator<Goods> { @Override
public int compare(Goods o1, Goods o2) {
return -(o1.getPrice()-o2.getPrice()>0?1:(o1.getPrice()==o2.getPrice()?0:-1));
} }
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; public class GoodsApp { /**
* @param args
*/
public static void main(String[] args) {
List<Goods> list =new ArrayList<Goods>();
list.add(new Goods("老马视频",100,2000));
list.add(new Goods("老高视频",50,2000));
list.add(new Goods("老裴视频",1000,1000));
System.out.println("排序前:"+list);
// Collections.sort(list,new GoodsPriceComp());
Collections.sort(list,new GoodsFavComp());
System.out.println("排序后:"+list);
} }
4、TreeSet TreeMap
- TreeMap:确保key可以排序或者提供灵活的比较器
- TreeSet:确保元素实体可以排序或者提供灵活比较器
Treeset 数据中的元素是排序的并且不能够重复,也是实现了Set接口,但和实现了Set接口的HashSet的是不同的,HashSet 是无序,也不可重复。都是不可以重复,但是它们判断的条件是不一样的,HashSet 的前面我们说到过,其元素中的类型时一定要实现hashCode 和equals 的方法来达到去重的目的,但是TreeSet 不一定要实现这两个方法,是通过比较器来实现的去重,即要么Comparable or Comparator 当元素比较返回值为0 即相等重复。
样例;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; public class Person {
private final String name;//名称
private final int handsome;//帅气指数 public Person() {
name =null;
handsome =0;
} public Person(String name, int handsome) {
super();
this.name = name;
this.handsome = handsome;
} public String getName() {
return name;
} public int getHandsome() {
return handsome;
} @Override
public String toString() {
return "姓名:"+this.name+",帅气指数:"+this.handsome+"\n";
} }
import java.util.TreeSet;
/**
* 提供了 解耦的方式:业务排序类
* @author Administrator
*
*/
public class TreeSetDemo { /**
* @param args
*/
public static void main(String[] args) {
Person p1 =new Person("您",100);
Person p2 =new Person("刘德华",1000);
Person p3 =new Person("梁朝伟",1200);
Person p4 =new Person("老裴",50); //依次存放到TreeSet容器中,使用排序的业务类(匿名内部类)
TreeSet<Person> persons =new TreeSet<Person>(
new java.util.Comparator<Person>(){ @Override
public int compare(Person o1, Person o2) {
return -(o1.getHandsome()-o2.getHandsome());
} }
);
persons.add(p1);
//TreeSet 在添加数据时排序
persons.add(p2);
persons.add(p3);
persons.add(p4); System.out.println(persons); /*
//改变数据
p4.setHandsome(100);
p4.setName("您");
*/
//p4 与p1 内容重复
System.out.println(persons); } }
注意点:在添加数据的时候进行排序,数据更改不会影响到原来的顺序,不要改变数据,否则可能重复。
样例2;采用实体类实现了Comparable接口:
public class Worker implements java.lang.Comparable<Worker> {
//工种
private String type;
//工资
private double salary;
public Worker() {
// TODO Auto-generated constructor stub
} public Worker(String type, double salary) {
super();
this.type = type;
this.salary = salary;
} public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
} /**
* 按工资升序
*/
@Override
public int compareTo(Worker o) {
return this.salary>o.salary?1:( this.salary==o.salary?0:-1);
} @Override
public String toString() {
return "工种:"+this.type+",工资:"+this.salary+"\n";
} }
import java.util.TreeSet;
/**
* 实体类实现Comparable 接口的应用
* @author Administrator
*
*/
public class TreeSetDemo2 { /**
* @param args
*/
public static void main(String[] args) {
Worker w1 =new Worker("垃圾回收员",12000);
Worker w2 =new Worker("农民工",8000);
Worker w3 =new Worker("程序猿",5000); TreeSet<Worker> employees =new TreeSet<Worker>();
employees.add(w1);
employees.add(w2);
employees.add(w3);
System.out.println(employees); } }
最后TreeMap的演示:
import java.util.Set;
import java.util.TreeMap; public class TreeMapDemo { /**
* @param args
*/
public static void main(String[] args) {
Person p1 =new Person("您",100);
Person p2 =new Person("刘德华",1000);
Person p3 =new Person("梁朝伟",1200);
Person p4 =new Person("老裴",50); TreeMap<Person,String> map =new TreeMap<Person,String>(new java.util.Comparator<Person>(){ @Override
public int compare(Person o1, Person o2) {
return -(o1.getHandsome()-o2.getHandsome());
} } );
map.put(p1, "bjsxt");
map.put(p2, "bjsxt");
map.put(p3, "bjsxt");
map.put(p4, "bjsxt"); //查看键
Set<Person> persons =map.keySet();
System.out.println(persons);
} }
import java.util.TreeMap; public class TreeMapDemo02 { /**
* @param args
*/
public static void main(String[] args) {
Worker w1 =new Worker("垃圾回收员",12000);
Worker w2 =new Worker("农民工",8000);
Worker w3 =new Worker("程序猿",5000); TreeMap<Worker,String > employees =new TreeMap<Worker,String >();
employees.put(w1,"bjsxt");
employees.put(w2,"bjsxt");
employees.put(w3,"bjsxt");
System.out.println(employees.keySet());
} }
四、Collections 工具类
首先,此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。它包含在 collection 上操作的多态算法,即“包装器”,包装器返回由指定 collection 支持的新 collection,以及少数其他内容。
如果为此类的方法所提供的 collection 或类对象为 null,则这些方法都将抛出 NullPointerException。
这个类提供了很多静态方法来辅助我们操作集合类,如:
方法摘要 | ||
---|---|---|
static
|
addAll(Collection<? super T> c, T... elements) 将所有指定元素添加到指定 collection 中。 |
|
static
|
asLifoQueue(Deque<T> deque) 以后进先出 (Lifo) Queue 的形式返回某个 Deque 的视图。 |
|
static
|
binarySearch(List<? extends Comparable<? super T>> list, 使用二分搜索法搜索指定列表,以获得指定对象。 |
|
static
|
binarySearch(List<? extends 使用二分搜索法搜索指定列表,以获得指定对象。 |
|
static
|
checkedCollection(Collection<E> c, Class<E> type) 返回指定 collection 的一个动态类型安全视图。 |
|
static
|
checkedList(List<E> list, 返回指定列表的一个动态类型安全视图。 |
|
static
|
checkedMap(Map<K,V> m, Class<K> keyType, Class<V> valueType) 返回指定映射的一个动态类型安全视图。 |
|
static
|
checkedSet(Set<E> s, Class<E> type) 返回指定 set 的一个动态类型安全视图。 |
|
static
|
checkedSortedMap(SortedMap<K,V> m, Class<K> keyType, Class<V> valueType) 返回指定有序映射的一个动态类型安全视图。 |
|
static
|
checkedSortedSet(SortedSet<E> s, Class<E> type) 返回指定有序 set 的一个动态类型安全视图。 |
|
static
|
copy(List<? super 将所有元素从一个列表复制到另一个列表。 |
|
static boolean |
disjoint(Collection<?> c1, Collection<?> c2) 如果两个指定 collection 中没有相同的元素,则返回 true。 |
|
static
|
emptyList() 返回空的列表(不可变的)。 |
|
static
|
emptyMap() 返回空的映射(不可变的)。 |
|
static
|
emptySet() 返回空的 set(不可变的)。 |
|
static
|
enumeration(Collection<T> c) 返回一个指定 collection 上的枚举。 |
|
static
|
fill(List<? super 使用指定元素替换指定列表中的所有元素。 |
|
static int |
frequency(Collection<?> c, Object o) 返回指定 collection 中等于指定对象的元素数。 |
|
static int |
indexOfSubList(List<?> source, List<?> target) 返回指定源列表中第一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。 |
|
static int |
lastIndexOfSubList(List<?> source, List<?> target) 返回指定源列表中最后一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。 |
|
static
|
list(Enumeration<T> e) 返回一个数组列表,它按返回顺序包含指定枚举返回的元素。 |
|
static
|
max(Collection<? extends 根据元素的自然顺序,返回给定 collection 的最大元素。 |
|
static
|
max(Collection<? extends T> coll, 根据指定比较器产生的顺序,返回给定 collection 的最大元素。 |
|
static
|
min(Collection<? extends 根据元素的自然顺序 返回给定 collection 的最小元素。 |
|
static
|
min(Collection<? extends T> coll, 根据指定比较器产生的顺序,返回给定 collection 的最小元素。 |
|
static
|
nCopies(int n, 返回由指定对象的 n 个副本组成的不可变列表。 |
|
static
|
newSetFromMap(Map<E,Boolean> map) 返回指定映射支持的 set。 |
|
static
|
replaceAll(List<T> list, 使用另一个值替换列表中出现的所有某一指定值。 |
|
static void |
reverse(List<?> list) 反转指定列表中元素的顺序。 |
|
static
|
reverseOrder() 返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序。 |
|
static
|
reverseOrder(Comparator<T> cmp) 返回一个比较器,它强行逆转指定比较器的顺序。 |
|
static void |
rotate(List<?> list, 根据指定的距离轮换指定列表中的元素。 |
|
static void |
shuffle(List<?> list) 使用默认随机源对指定列表进行置换。 |
|
static void |
shuffle(List<?> list, 使用指定的随机源对指定列表进行置换。 |
|
static
|
singleton(T o) 返回一个只包含指定对象的不可变 set。 |
|
static
|
singletonList(T o) 返回一个只包含指定对象的不可变列表。 |
|
static
|
singletonMap(K key, 返回一个不可变的映射,它只将指定键映射到指定值。 |
|
static
|
sort(List<T> list) 根据元素的自然顺序 对指定列表按升序进行排序。 |
|
static
|
sort(List<T> list, 根据指定比较器产生的顺序对指定列表进行排序。 |
|
static void |
swap(List<?> list, 在指定列表的指定位置处交换元素。 |
|
static
|
synchronizedCollection(Collection<T> c) 返回指定 collection 支持的同步(线程安全的)collection。 |
|
static
|
synchronizedList(List<T> list) 返回指定列表支持的同步(线程安全的)列表。 |
|
static
|
synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全的)映射。 |
|
static
|
synchronizedSet(Set<T> s) 返回指定 set 支持的同步(线程安全的)set。 |
|
static
|
synchronizedSortedMap(SortedMap<K,V> m) 返回指定有序映射支持的同步(线程安全的)有序映射。 |
|
static
|
synchronizedSortedSet(SortedSet<T> s) 返回指定有序 set 支持的同步(线程安全的)有序 set。 |
|
static
|
unmodifiableCollection(Collection<? extends 返回指定 collection 的不可修改视图。 |
|
static
|
unmodifiableList(List<? extends 返回指定列表的不可修改视图。 |
|
static
|
unmodifiableMap(Map<? extends K,? 返回指定映射的不可修改视图。 |
|
static
|
unmodifiableSet(Set<? extends 返回指定 set 的不可修改视图。 |
|
static
|
unmodifiableSortedMap(SortedMap<K,? extends 返回指定有序映射的不可修改视图。 |
|
static
|
unmodifiableSortedSet(SortedSet<T> s) 返回指定有序 set 的不可修改视图。 |
其中最主要的一个方法,也是本节关注点,就是sort 排序,在对Java无序类集合,如List(ArrayList/LinkedList)、HashSet(TreeSet有序)、HashMap等排序时,Java中一个公共的类Collections,提供了对Java集合排序等很好的方法sort。 但是有一个要求是sort方法的参数为<List list> 或<List list, Comparator<? super T> c>,即排序对象要求必须是List类型。
sort 方法的参数必须为List 的原因是,只有List可以定义排序的方法,让List中的元素改变在构建List时原始的相对位置(初始构建时,元素相对位置即为元素初始加入顺序)。HashSet、HashMap 在构建时,初始加入的元素已经按照元素的hashCode()方法的定义排好序。所以这里所说的HashSet 排序 和 HashMap 排序是指:将其中的元素导出到另一个集合中,对该载体集合排序。排序之后,原HashSet 和 HashMap 中元素顺序没有变。
故而对Java无序类集合的排序问题,基本思路就是:将HashSet 或 HashMap 中的元素取出放入 List 中,对List 用 Collections.sort() 方法排序,之后输出排序后List中的元素,即为对Set/Map 中元素排序后的结果。注意HashSet、HashMap 中元素位置没有改变,依然只和 初始构建时,元素本身自定义的hashCode() 方法有关。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map; public class Test {
public static void main(String[] args){
ArrayList<String> listTest = new ArrayList<String>();
listTest.add("bbc");
listTest.add("abc");
listTest.add("acb"); HashSet<String> setTest = new HashSet<String>();
setTest.add("bbc");
setTest.add("abc");
setTest.add("acb");
System.out.println("HashSet BeforeSort:");
for(String s : setTest)
System.out.println(s); HashMap<String, Integer> mapTest = new HashMap<String, Integer>();
mapTest.put("bbc", 1);
mapTest.put("abc", 2);
mapTest.put("acb", 3);
System.out.println("HashMap BeforeSort:");
for(Map.Entry<String, Integer> entry : mapTest.entrySet())
System.out.println(entry.getKey() + " " + entry.getValue()); /*
* List
*/
Collections.sort(listTest);
Iterator<String> list_iter = listTest.iterator();
while(list_iter.hasNext())
System.out.println(list_iter.next()); /*
* Set
*/
LinkedList<String> setSort = new LinkedList<String>(setTest);
//Collections.sort(setSort);
Comparator<String> setComp = Collections.reverseOrder();
Collections.sort(setSort, setComp);
/*LinkedList<String> setSort = new LinkedList<String>();
for(String s : setTest)
setSort.add(s);*/
for(String s : setTest)
System.out.println(s);
for(String s : setSort)
System.out.println(s); /*
* Map
*/
LinkedList<String> mapSort = new LinkedList<String>();
mapSort.addAll(mapTest.keySet());
//Collections.sort(mapSort);
Comparator<String> mapComp = Collections.reverseOrder();
Collections.sort(mapSort, mapComp);
for(Map.Entry<String, Integer> entry : mapTest.entrySet())
System.out.println(entry.getKey() + " " + entry.getValue());
for(final Iterator<String> map_iter= mapSort.iterator(); map_iter.hasNext();)
System.out.println(map_iter.next());
/*
LinkedList<Map.Entry<String, Integer>> mapEntry = new LinkedList<Map.Entry<String,Integer>>();
mapEntry.addAll(mapTest.entrySet());
Collections.sort(mapEntry, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b){
if(a.getValue() > b.getValue())
return -1;
else
return 1;
}
}); for(Map.Entry<String, Integer> entry : mapEntry)
System.out.println(entry.getKey() + " " +entry.getValue());
for(Map.Entry<String, Integer> entry : mapTest.entrySet())
System.out.println(entry.getKey() + " " + entry.getValue());*/
}
}
结果;
HashSet BeforeSort:
abc
acb
bbc
HashMap BeforeSort:
abc 2
acb 3
bbc 1
//List AfterSort
abc
acb
bbc
//HashSet AfterSort
abc
acb
bbc
//setSort AfterSort (setSort is means HashSet to LinkedList)
bbc
acb
abc
//HashMap AfterSort
abc 2
acb 3
bbc 1
//mapSort AfterSort (mapSort is means HashMap to LinkedList)
bbc
acb
abc
小节:
一、按key值排序
假设HashMap存储的键-值对为(String,Integer),按key排序可以调用JDK函数sort(默认的按字典升序):
Set<String> keySet = map.keySet();
Collections.sort(keySet);
for(Iterator<String> ite = keySet.iterator(); ite.hasNext();) {
String temp = ite.next();
System.out.println("key-value: "+temp+","+map.getValue(temp);
}
如果想要按字典的降序排列,则需改写sort方法里面的比较器Comparator:
Collections.sort(keySet, new Comparator() {
public int compare(Object o1, Object o2) {
if(Integer.parseInt(o1.toString())>Integer.parseInt(o2.toString())
return 1;
if(Integer.parseInt(o1.toString())==Integer.parseInt(o2.toString())
return 0;
else
return -1;
}
}); 二、按value值排序
1)方法一:用两个list链表实现
List<String> keyList = new LinkedList<String>();
keyList.addAll(map.keySet());
List<Integer> valueList = new LinkedList<Integer>();
valueList.addAll(map.values());
for(int i=0; i<valueList.size(); i++)
for(int j=i+1; j<valueList.size(); j++) {
if(valueList.get(j)>valueList.get(i)) {
valueList.set(j, valueList.get(i));
valueList.set(i, valueList.get(j));
//同样调整对应的key值
keyList.set(j, keyList.get(i));
keyList.set(i, kyeList.get(j));
}
然后依次把key值和对应value值重新装入HashMap即可。
2)方法二:改写JDK提供的Comparator接口方法compare
List<Map.Entry<String, Integer>> list = new LinkedList<Map.Entry<String, Integer>>();
list.addAll(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry obj1, Map.Entry obj2) {//从高往低排序 if(Integer.parseInt(obj1.getValue().toString())<Integer.parseInt(obj2.getValue().toString()))
return 1;
if(Integer.parseInt(obj1.getValue().toString())==Integer.parseInt(obj2.getValue().toString()))
return 0;
else
return -1;
}
});
for(Iterator<Map.Entry<String, Integer>> ite = list.iterator(); ite.hasNext();) {
Map.Entry<String, Integer> map = ite.next();
System.out.println("key-value: " + map.getKey() + "," + map.getValue());
}
Java提高十五:容器元素比较Comparable&Comparator深入分析的更多相关文章
- JAVA提高十九:WeakHashMap&EnumMap&LinkedHashMap&LinkedHashSet深入分析
因为最近工作太忙了,连续的晚上支撑和上班,因此没有精力来写下这篇博客,今天上午正好有一点空,因此来复习一下不太常用的集合体系大家族中的几个类:WeakHashMap&EnumMap&L ...
- Java提高十六:TreeMap深入分析
上一篇容器元素比较Comparable&Comparator分析的时候,我们提到了TreeMap,但没有去细致分析它,只是说明其在添加元素的时候可以进行比较,从而使得集合有序,但是怎么做的呢? ...
- “全栈2019”Java第九十五章:方法中可以定义静态局部内部类吗?
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第十五章:Unicode与转义字符
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第二十五章:流程控制语句中循环语句while
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- JAVA提高十八:Vector&Stack深入分析
前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈. 什么是栈呢,我们先看一个例子:栈就相当于一个很窄的木桶,我们往木桶里放东西,往外拿东 ...
- JAVA提高十:ArrayList 深入分析
前面一章节,我们介绍了集合的类图,那么本节将学习Collection 接口中最常用的子类ArrayList类,本章分为下面几部分讲解(说明本章采用的JDK1.6源码进行分析,因为个人认为虽然JDK1. ...
- JAVA提高十二:HashMap深入分析
首先想说的是关于HashMap源码的分析园子里面应该有很多,并且都是分析得很不错的文章,但是我还是想写出自己的学习总结,以便加深自己的理解,因此就有了此文,另外因为小孩过来了,因此更新速度可能放缓了, ...
- java提高篇(五)-----抽象类与接口
接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 抽象类与接口是java语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予java强大的面向对象的能力.他们两者之间对抽象概念 ...
随机推荐
- IIS配置發佈網站常見問題及設置
解决方法: 修改.NET Framework 版本为相应版本即可,我以前用的是2.0换成4.0的时候出现这个错误. 我的win7系统, 1.打开IIs点击IIS根节点 2.看右边的“操作”->点 ...
- win10 uwp DataContext
本文告诉大家DataContext的多种绑法. 适合于WPF的绑定和UWP的绑定. 我告诉大家很多个方法,所有的方法都有自己的优点和缺点,可以依靠自己喜欢的用法使用.当然,可以在新手面前秀下,一个页面 ...
- 【转】花开正当时,十四款120/128GB SSD横向评测
原文地址:http://www.expreview.com/19604-all.html SSD横评是最具消费指导意义的评测文章,也是各类热门SSD固态硬盘的决斗疆场.SSD评测在行业内已经有不少网站 ...
- Hadoop Streaming详解
一: Hadoop Streaming详解 1.Streaming的作用 Hadoop Streaming框架,最大的好处是,让任何语言编写的map, reduce程序能够在hadoop集群上运行:m ...
- webpack安装教程及实例
在控制台输入: npm install webpack -g 这是全局的安装,如果需要局部安装,在控制台cd 打开到指定目录,输入: npm install webpack --save-dev 即可 ...
- 如何在Pypi上发表自己的Python库
背景 最近兴趣使然写了几个Python库,也发布到了Pypi上,虽然没什么人下载,但自己在其他机器上用着也会很方便.这里我向大家介绍一下如何在Pypi上发表自己的Python库. 准备 注册账号 很显 ...
- Java 链表常见考题总结
首先定义自定义结点类,存储节点信息: public class Node { Node next=null; int data; public Node(int data){ this.data=da ...
- HDU 1074 Doing Homework (状态压缩DP)
Doing Homework Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)To ...
- awk内置函数
gsub(r,s,t) 在字符串t中,用字符串s替换和正则表达式r匹配的所有字符串.返回替换的个数.如果没有给出t,缺省为$0 index(s,t) 返回s 中字符串t 的位置,不出现时为0 leng ...
- File API简介
File API让我们可以创建文件,存储在本地文件系统的一个安全沙箱里,亦可以从其他来源读取文件,并对其进行操作 Web应用通过requestFileSystem方法来访问本地文件系统,该方法是全 ...