以前对List集合进行排序的时候都用Collections类的sort方法, 在jdk1.8出来后就常用Lambda表达式来实现对list集合的排序
Lambda表达式的介绍如下:
“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。
通俗的来讲就是匿名函数,函数式编程,这也是jdk1.8最大的变化,用Lambda语法来替换以前的匿名内部类,代码量会减少很多看起来也更整洁。
先介绍下Collections
介绍Collections之前先说下跟它名字很接近的一个接口Collection(在jdk1.2的时候就已经存在了),此接口是是一个集合接口,集合类的最顶级接口,提供了集合对象进行操作的通用性方法(图一),Collection接口在java类库中有很多实现,直接继承与此接口的有List和Set接口(图二,图四),另外,此Collection还继承Iterable接口(图三),Iterable接口是用来对集合进行迭代的(通常我们遍历集合的时候为了效率都采用迭代器而不用foreach之类进行遍历,此接口是在jdk1.5的时候专门增加的)
图一:
图二:
图三:
图四:
现在说下Collections是实现Collection接口的集合类的工具类或者帮助类(图5),可以看到此类在jdk1.2的时候已经存在,并且无参构造是私有的,表明此类不允许实例化,并且提供了一系列静态方法供外部调用以实现对集合排序、搜索等操作
图5:
现在就通过Collections类中的sort方法实现对集合的排序:
场景是这样的:
下拉框 “来源” 里面的信息是动态获取的(例图1),从字典表“t_resource_dic”中获取,如果有返回信息的话加上“全部”,如果没有返回信息的话,就不加上“全部”,所以需要在从库里获取信息后才能确定是否加上“全部”,有两种方式可以实现,要么在后端造一个“全部”的一条数据,要么是在前端造“全部”的一条数据,而我选择通过后端实现从小到大的顺序(代码2),在ResourceDic类造一条数据,id设置为0,但此时 resourceDicList.add()新造的这条数据位于最末尾,需要排序到第一位。
我要对 ResourceDic类(代码1)按照id进行排序(前端展示的时候从0开始),那么我首先想到了Collections工具类的sort方法,第一个参数是List集合,第二个参数需要实现一个比较器 Comparator(英文:n 名词,比较仪)接口,采用匿名内部类的方式实现,完成从小到大的排序。但是通过查看Collections的源码发现有两个重载sort方法入参不同(源码1),只有一个入参的sort方法需要让集合中的泛型类实现Comparable接口,并重写ComparaTo方法。
下面着重介绍下Comparator和Comparable两个接口的用法和不同:
往下看:
例图1
--代码1--
package com.chehejia.cms.web.model;
import java.io.Serializable;
import lombok.*;
@Getter
@Setter
public class ResourceDic implements Serializable {
private Long id;
private String resourceName;
private Integer resourceType;
private Integer dicType;
private String dicName;
public ResourceDic() {
}
public ResourceDic(Long id, String resourceName, Integer resourceType) {
this.id = id;
this.resourceName = resourceName;
this.resourceType = resourceType;
}
private static final long serialVersionUID = 1L;
}
代码2
/**
* 获取优惠来源
* @return
*/
@Override
public Result<Object> getSourceList() {
List<ResourceDic> resourceDicList = this.resourceDicMapper.getSourceList(BaseEnums.RESOURCE_MEMBER_DIC_TYPE.getCode());
if (!CollectionUtils.isEmpty(resourceDicList)) {
resourceDicList.add(new ResourceDic(Integer.toUnsignedLong(BaseEnums.ALL_ROURCE.getCode()),BaseEnums.ALL_ROURCE.getDesc(),BaseEnums.ALL_ROURCE.getCode()));
Collections.sort(resourceDicList, new Comparator<ResourceDic>() {
@Override
public int compare(ResourceDic o1, ResourceDic o2) {
/** 从小到大的顺序 */
return o1.getId().compareTo(o2.getId());
}
});
}
log.info("service层获取优惠来源, 结果: resourceDicList = {}",resourceDicList);
return Results.success(resourceDicList);
}
源码1
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
/**
* Sorts the specified list according to the order induced by the
* specified comparator. All elements in the list must be <i>mutually
* comparable</i> using the specified comparator (that is,
* {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
* for any elements {@code e1} and {@code e2} in the list).
*
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* <p>The specified list must be modifiable, but need not be resizable.
*
* @implNote
* This implementation defers to the {@link List#sort(Comparator)}
* method using the specified list and comparator.
*
* @param <T> the class of the objects in the list
* @param list the list to be sorted.
* @param c the comparator to determine the order of the list. A
* {@code null} value indicates that the elements' <i>natural
* ordering</i> should be used.
* @throws ClassCastException if the list contains elements that are not
* <i>mutually comparable</i> using the specified comparator.
* @throws UnsupportedOperationException if the specified list's
* list-iterator does not support the {@code set} operation.
* @throws IllegalArgumentException (optional) if the comparator is
* found to violate the {@link Comparator} contract
* @see List#sort(Comparator)
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
Comparator和Comparable接口都是实现排序的
首先, 先看Comparator,英文的意思为:“n. [仪] 比较仪;比测仪”,很明显这是个名词,哈哈,根据英文的语法内涵,此接口用匿名内部类的方式实现排序最合适(“比较器”拿来直接用),事实证明学好英文对深入编程是很重要的,看源码2知道此接口也是jdk1.2的时候增加的(@FunctionalInterface注解是jdk1.8增加的,变成了函数形式接口--这意味着后面的代码用Lambda表达式来实现排序可以用到Comparator接口,1.8重要的改变就是函数式编程(源码3))。
所以,只要实现@FunctionalInterface注解的接口都是函数式接口,都可以用Lambda表达式来实现操作,是代码变得更简洁,
其次, Comparable,英文的意思为:“adj. 可比较的;比得上的”,很明显这是个形容词,再哈哈,根据英文的语法内涵,“可比较的”修饰为名词后再拿来用,那么用实体类来实现Comparable这个接口,完成“修饰的”是最合适。此接口是jdk1.2增加的(源码4),并且只有一个方法 compareTo,注意此接口上没有@FunctionalInterface注解,所以此接口不是函数式接口,后续的博客我会专门针对jdk1.8的新功能做讲解
接下来我的 ResourceDic类实现了Comparable接口(代码3),并重写了compareTo方法,实现对id的从小到大排序,在service层只需用Collections类的一个入参的sort方法(代码4)
接下来讲下用jdk1.8新增的Lambda表达式实现排序,用函数式编程来完成。
List接口在jdk1.8中新增了一个sort方法(图6),入参为 Comparator 接口的对象,实现代码(代码5),只需要通过 resourceDicList.sort((r1, r2) -> r1.getId().compareTo(r2.getId())); 这一行代码就可以实现,瞬间就变得很简洁,但是刚用 函数式 编程会感觉很不习惯,没关系,先学会用,慢慢就会领悟到。
实际上Lambda中的λ表达式本质上是一个匿名方法,λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。如“resourceDicList.sort((r1, r2) -> r1.getId().compareTo(r2.getId()));”r1和r2是参数列表,箭头 ->,r1.getId().compareTo(r2.getId())是语句块。其中表达式执行后会返回结果, 语句块中的语句会被依次的执行,和普通方法中的语句是一样的效果。
先写到这里了,总之jdk1.8的Lambda表达式的函数式编程是个非常有趣的东西,值得我们去深入研究并予以熟练应用,如果不懂不熟练应用1.8新增的功能,可能会被淘汰调的, 哈哈, 周末专门写这篇文章,周末愉快。
源码2
*
* @author Josh Bloch
* @author Neal Gafter
* @see Comparable
* @see java.io.Serializable
* @since 1.2
*/
@FunctionalInterface
public interface Comparator<T> {
/**
* Compares its two arguments for order. Returns a negative integer,
* zero, or a positive integer as the first argument is less than, equal
* to, or greater than the second.<p>
*
* In the foregoing description, the notation
* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
* <tt>0</tt>, or <tt>1</tt> according to whether the value of
* <i>expression</i> is negative, zero or positive.<p>
*
* The implementor must ensure that <tt>sgn(compare(x, y)) ==
* -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
* implies that <tt>compare(x, y)</tt> must throw an exception if and only
* if <tt>compare(y, x)</tt> throws an exception.)<p>
*
* The implementor must also ensure that the relation is transitive:
* <tt>((compare(x, y)>0) && (compare(y, z)>0))</tt> implies
* <tt>compare(x, z)>0</tt>.<p>
*
* Finally, the implementor must ensure that <tt>compare(x, y)==0</tt>
* implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
* <tt>z</tt>.<p>
*
* It is generally the case, but <i>not</i> strictly required that
* <tt>(compare(x, y)==0) == (x.equals(y))</tt>. Generally speaking,
* any comparator that violates this condition should clearly indicate
* this fact. The recommended language is "Note: this comparator
* imposes orderings that are inconsistent with equals."
*
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
* @throws NullPointerException if an argument is null and this
* comparator does not permit null arguments
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
*/
int compare(T o1, T o2);
/**
* Indicates whether some other object is "equal to" this
* comparator. This method must obey the general contract of
* {@link Object#equals(Object)}. Additionally, this method can return
* <tt>true</tt> <i>only</i> if the specified object is also a comparator
* and it imposes the same ordering as this comparator. Thus,
* <code>comp1.equals(comp2)</code> implies that <tt>sgn(comp1.compare(o1,
* o2))==sgn(comp2.compare(o1, o2))</tt> for every object reference
* <tt>o1</tt> and <tt>o2</tt>.<p>
*
* Note that it is <i>always</i> safe <i>not</i> to override
* <tt>Object.equals(Object)</tt>. However, overriding this method may,
* in some cases, improve performance by allowing programs to determine
* that two distinct comparators impose the same order.
*
* @param obj the reference object with which to compare.
* @return <code>true</code> only if the specified object is also
* a comparator and it imposes the same ordering as this
* comparator.
* @see Object#equals(Object)
* @see Object#hashCode()
*/
boolean equals(Object obj);
源码3:
*
* @jls 4.3.2. The Class Object
* @jls 9.8 Functional Interfaces
* @jls 9.4.3 Interface Method Body
* @since 1.8
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
源码4:
* @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> {
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
* -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
* implies that <tt>x.compareTo(y)</tt> must throw an exception iff
* <tt>y.compareTo(x)</tt> throws an exception.)
*
* <p>The implementor must also ensure that the relation is transitive:
* <tt>(x.compareTo(y)>0 && y.compareTo(z)>0)</tt> implies
* <tt>x.compareTo(z)>0</tt>.
*
* <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
* implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
* all <tt>z</tt>.
*
* <p>It is strongly recommended, but <i>not</i> strictly required that
* <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>. Generally speaking, any
* class that implements the <tt>Comparable</tt> interface and violates
* this condition should clearly indicate this fact. The recommended
* language is "Note: this class has a natural ordering that is
* inconsistent with equals."
*
* <p>In the foregoing description, the notation
* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
* <tt>0</tt>, or <tt>1</tt> according to whether the value of
* <i>expression</i> is negative, zero or positive.
*
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*
* @throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
*/
public int compareTo(T o);
}
代码3:
package com.chehejia.cms.web.model;
import java.io.Serializable;
import lombok.*;
@Getter
@Setter
public class ResourceDic implements Serializable, Comparable<ResourceDic> {
private Long id;
private String resourceName;
private Integer resourceType;
private Integer dicType;
private String dicName;
@Override
public int compareTo(ResourceDic o) {
return this.getId().compareTo(o.getId());
}
public ResourceDic() {
}
public ResourceDic(Long id, String resourceName, Integer resourceType) {
this.id = id;
this.resourceName = resourceName;
this.resourceType = resourceType;
}
private static final long serialVersionUID = 1L;
}
代码4:
/**
* 获取优惠来源
* @return
*/
@Override
public Result<Object> getSourceList() {
List<ResourceDic> resourceDicList = this.resourceDicMapper.getSourceList(BaseEnums.RESOURCE_MEMBER_DIC_TYPE.getCode());
if (!CollectionUtils.isEmpty(resourceDicList)) {
resourceDicList.add(new ResourceDic(Integer.toUnsignedLong(BaseEnums.ALL_ROURCE.getCode()),BaseEnums.ALL_ROURCE.getDesc(),BaseEnums.ALL_ROURCE.getCode()));
Collections.sort(resourceDicList);
}
log.info("service层获取优惠来源, 结果: resourceDicList = {}",resourceDicList);
return Results.success(resourceDicList);
}
图6:
代码5:
/**
* 获取优惠来源
* @return
*/
@Override
public Result<Object> getSourceList() {
List<ResourceDic> resourceDicList = this.resourceDicMapper.getSourceList(BaseEnums.RESOURCE_MEMBER_DIC_TYPE.getCode());
if (!CollectionUtils.isEmpty(resourceDicList)) {
resourceDicList.add(new ResourceDic(Integer.toUnsignedLong(BaseEnums.ALL_ROURCE.getCode()),BaseEnums.ALL_ROURCE.getDesc(),BaseEnums.ALL_ROURCE.getCode()));
resourceDicList.sort((r1, r2) -> r1.getId().compareTo(r2.getId()));
}
log.info("service层获取优惠来源, 结果: resourceDicList = {}",resourceDicList);
return Results.success(resourceDicList);
}