1.compareTo用于TreeSet,而TreeSet个人认为最好用于读DB的结果集,将来可以基于compareTo的排序结果取subSet。写DB的时候,可以使用HashSet而不是TreeSet,因为写之前的结构不需要考虑排序与取子集。
2.compareTo如何实现:按照想要比较的字段的冲突出现的概率依次判断。
假设表的结构:memberid, locationid,type 并且memberid,locatonid是组合主键。这样一个memberid可以对应多条locationid的记录。
如果从DB中读出某个member的所有记录放到TreeSet,并且将来根据type排序,那么type需要第一个出现在compareTo里面,同时只有type,其它相同type的记录可能就添加不到treeset里,所以还需要拉上一个类似于identify的字段,当然就是locationid。
这样想要比较的字段就是type,locationid,根据冲突出现的可能性,当然是先type,后locationid。
示例: MemberLocationElement对象放到TreeSet中的compareTo的实现策略:先判断最可能相同的type值,然后再判断最不可能冲突的locationid。TreeSet主要用于读从DB读出结果集时使用。在写入的时候,需要保证locationid不同,可以使用HashSet来写。所以,TreeSet个人觉得最好的方式是读的时候使用。而不是希望它做add的操作。
public static class MemberLocationElementComparator implements Comparator<MemberLocationElement>,Serializable{ private static final long serialVersionUID = 1L; @Override public int compare(MemberLocationElement o1, MemberLocationElement o2) { if (o1 == null || o2 == null) return -1; if (o1.getType().getVal() == o2.getType().getVal()) { return o1.getLocationid() - o2.getLocationid(); } return o1.getType().getVal() - o2.getType().getVal(); } }
又比如从DB读Person记录到TreeSet,Person包含fn以及ln二个属性分别表示first_name,last_name. 假设fn+ln构造复合主键。如果要读所有的用户记录到TreeSet
此时compareTo的字段应该是fn,ln,注意这里的ln并不是一个identify的字段,但是由于fn+ln是identify,按照冲突出现的概率,fn冲突的可能性更大,所以先比较。
比如加入李想,李晨, 不能因为加入李想就由于first_name=李而不让加入李晨,所以如果fn相等,可以接着比较ln。
对于不同的fn,当然可以加入到TreeSet,但是对于相同的fn,此时就需要接着比较last_name以防止不能加入。
又比如图片类PhotoInfo包含了blessed和uploaddate的值,blessed的值从0~7,如果实现一个2>1>6的排序,相同情况下再按uploaddate排序(最近的先显示)
比较器实现如下:
public static class TestComparator implements Comparator<PhotoInfo>, Serializable { private static final long serialVersionUID = 1L; public static final Map<Integer, Integer> blessedIndMap = new HashMap<Integer, Integer>(); static { blessedIndMap.put(UserImage.S_BLESSED_GEO_PARENT,3); blessedIndMap.put(UserImage.S_BLESSED_GEO_ONLY,2); blessedIndMap.put(UserImage.S_BLESSED_PROPERTY_ONLY,1); } @Override public int compare(PhotoInfo o1, PhotoInfo o2) { if (o1 == null || o2 == null) { return -1; } int bind1 = getIndexByBlessed(o1.getBlessed()); int bind2 = getIndexByBlessed(o2.getBlessed()); if (bind1 == bind2) { //recently first show,因为默认都是升序,所以取负值。 return -(o1.getUploadedDate().compareTo(o2.getUploadedDate())); } //blessed show order 2,1,6,other,因为默认都是升序,所以取负值。 return -(bind1 - bind2); } public int getIndexByBlessed(int blessed) { return blessedIndMap.get(blessed) == null ? 0 : blessedIndMap.get(blessed); } }
对于List,使用比较器如下:Collections.sort(photoInfoList, new TestComparator());
3.特别强调Map通过comparator只能比较value,如果想让key也排序或者是做到有序Map,就只能使用LinkedHashMap,它支持按照添加的顺序排序。
4.TreeSet的接口划分及使用:
对于remove, removeAll这样的操作依赖于Element定义的equal方法。
对于add,addAll,headSet, tailSet,subSet这样操作依赖于compareTo方法。
取某个范围的结果集使用tailSet+headSet。subSet取的是半开半闭区间的结果集,不好操作。tailSet表示>= headSet表示<
TreeSet是实现类,根据排序取范围的操作都定义在接口SortedSet中,建议使用SortedSet而不是具体类。
import java.util.Comparator; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; public class TreeSetTest { /** * @param args */ public enum Type{ GOING(1),BEEN(2),GOING_THEN_BEEN(3); private int val; private Type(int val){ this.val=val; } public int getVal() { return val; } public void setVal(int val) { this.val = val; } } public static class Element{ private int memeberid; private int locationid; private Type type; public Element(){} public int getMemeberid() { return memeberid; } public void setMemeberid(int memeberid) { this.memeberid = memeberid; } public int getLocationid() { return locationid; } public void setLocationid(int locationid) { this.locationid = locationid; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public Element(int memeberid, int locationid, Type type) { super(); this.memeberid = memeberid; this.locationid = locationid; this.type = type; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + locationid; result = prime * result + memeberid; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Element other = (Element) obj; if (locationid != other.locationid) return false; if (memeberid != other.memeberid) return false; return true; } } public static void main(String[] args) { TreeSet<Element> elements=new TreeSet<Element>(new Comparator<Element>() { @Override public int compare(Element o1, Element o2) { if(o1.getType().getVal()==o2.getType().getVal()){ return o1.getLocationid()-o2.getLocationid(); } return o1.getType().getVal()-o2.getType().getVal(); } }); Element e1=new Element(111111, 294211, Type.GOING); Element e2=new Element(111111, 294212, Type.BEEN); Element e3=new Element(111111, 294213, Type.BEEN); Element e4=new Element(111111, 294214, Type.GOING_THEN_BEEN); elements.add(e1); elements.add(e2); elements.add(e3); elements.add(e4); loadSet(elements); System.out.println("-----------------------"); Element from=new Element(); from.setType(Type.GOING); Element to=new Element(); to.setType(Type.GOING_THEN_BEEN); Element too=new Element(); too.setType(Type.BEEN); Set<Element> subSet=elements.tailSet(too).headSet(to); // Set<Element> subSet=elements.tailSet(to); // Set<Element> subSet=elements.tailSet(too); // Set<Element> subSet=elements.subSet(from,Boolean.FALSE, to,Boolean.FALSE); loadSet(subSet); System.out.println("-------------------------"); HashSet<Element> rmset=new HashSet<Element>(); e2.setType(Type.GOING_THEN_BEEN); rmset.add(e2); e3.setType(Type.GOING_THEN_BEEN); rmset.add(e3); //其实是逐个调用remove方法,remove方法的根据是 如果此 set 中包含满足 (o==null ? e==null : o.equals(e)) 的元素 e,则移除它。可以看到remove这样不涉及到排序的操作equal是起作用的。 elements.removeAll(rmset); elements.addAll(rmset); loadSet(elements); } private static void loadSet(Set<Element> set) { for (Element element : set) { System.out.println(element.locationid+"|"+element.getType().name()); } } }