图片 5

Java排序详解

本文由码农网 –
小峰原创翻译,转发请看清文末的转发供给,招待加入大家的付费投稿计划!

很难想象有Java开荒职员不曾使用过Collection框架。在Collection框架中,首要使用的类是来源于List接口中的ArrayList,以致源于Set接口的HashSet、TreeSet,大家日常拍卖那几个Collections的排序。

聚焦框架的现身

集中框架,在平时的编制程序中时时会用到,种类宏大又特别的关键,所以做个总括很有供给,大家知道数组能够用来寄放在底子数据类型与引用类型,在概念的时候,寄放在数组中的类型是已经规定的了,如上边给定的数组只好用来寄放String类型的值,並且那时候数组的长度已经定位

String[] array = new String[5];

固然您计划在array中赋于String以外的种类,则会提交编写翻译提醒,类型转变错误

图片 1

又举例你想给数组加多二个新的值,譬喻说像这么

array[5] = "s";

那正是说在运作时,设想机缘抛出如下格外,数组越界

图片 2

为了弥补数组这种不可变的行事,Java设计者们设计了一三种操作成分的集结类,这一多元的群集类就重新组合了集聚框架,由于这当中涉及到的接口及类相当多,所以须求先对全体体系有三个感观的认知,那样才干了然入怀。

很难想象有Java开垦人士不曾使用过Collection框架。在Collection框架中,首要运用的类是发源List接口中的ArrayList,以至来自Set接口的HashSet、TreeSet,大家日常管理这几个Collections的排序。

小编收拾了有个别java进级学习质感和面试题,供给质地的请加JAVA高阶学习Q群:664389243
那是作者创立的java高阶学习调换群,加群一同交换学习深造。群里也会有笔者收拾的今年流行最全的java高阶学习资料!

集合的三结合

 图片 3

下面那张图只是简短的列出了聚众中最常使用的接口以至贯彻类,当然远远不唯有这几个,可是通晓了这么些就会一蹴而就半数以上的聚众难点。从图中,能够精晓集结框架中任重(Ren ZhongState of Qatar而道远有两大流派,那多少个派系被架空成了多个接口,分别为Collection与Map,那七个接口下边又衍生出子接口以致一些落到实处类,对于List、Set和Map二种集结,最常用的兑现类分别是ArrayList、HashSet和HashMap多个落实类。直面广大忙乱的各样接口以至类,怎么本事相比好的左右呢,其实能够把集结框架作为是三个小型的数据库,我们都理解,数据库重视的操作正是增加和删除改查,那么聚焦也是如此,从那五个地点入手,就可以差不离精晓集结的用法。

在本文中,我将根本关切排序Collection的ArrayList、HashSet、TreeSet,以至最后但毫无最不首要的数组。

在本文中,笔者将主要关怀排序Collection的ArrayList、HashSet、TreeSet,以致最后但不要最不重大的数组。

List

图片 4

大家看看怎样对给定的大背头会集(5,10,0,-1)实行排序:

ArrayList

ArrayList类为List接口的首要实现类,是三个数组成代表队列,提供了增进、删除、更改、遍历等效果,相当于是贰个动态数组,与数组的一定容积相相比较,ArrayList集结的容积能够依照早晚的平整动态的加强,完成了RandmoAccess接口,即提供了狂妄访谈成效。在ArrayList中,大家可以透过成分的序号飞快获得成分对象,那正是全速随机访问

ArrayList中的方法很多都跟地方有关,上面的代码列举了ArrayList一些常用的点子

package collection.list;

import java.util.ArrayList;
import java.util.List;

public class ListDemo1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        // 添加元素
        list.add("红楼梦");
        list.add("三国演义");
        list.add("西游记");
        list.add("水浒传");

        // 获取集合大小
        System.out.println("初始状态:" + list);
        System.out.println("数组大小:" + list.size());

        // 判断集合是否包含某元素
        boolean contain = list.contains("水浒传");
        System.out.println("集合是否包含水浒传: " + contain);

        // 判断集合是否为空
        boolean isEmpty = list.isEmpty();
        System.out.println("集合是否为空:" + isEmpty);

        // 获取第一个元素
        String string = list.get(0);
        System.out.println("第一个元素是: " + string);

        // 获取第一次出现给定元素的位置
        int index = list.indexOf("水浒传");
        System.out.println("第一次出现水浒传的位置是:" + index);

        // 在1位置插入元素,插入的位置不能超过集合的最大长度,否则报java.lang.IndexOutOfBoundsException异常
        list.add(2, "平凡的世界");
        System.out.println("在位置2处插入元素后的集合: " + list);

        // 删除刚才插入的元素
        string = list.remove(2);
        System.out.println("被删除的元素: " + string);
        System.out.println("删除元素后的集合: " + list);

        // 替换指定位置的元素
        string = list.set(0, "儒林外史");
        System.out.println("被替换的元素是:" + string);
        System.out.println("现在集合中的元素是:" + list);

        // 清空集合
        list.clear();
        System.out.println("清空集合后:" + list);
        System.out.println("清空集合是否为空:" + list.isEmpty());
    }
}

运转结果

初始状态:[红楼梦, 三国演义, 西游记, 水浒传]
数组大小:4
集合是否包含水浒传: true
集合是否为空:false
第一个元素是: 红楼梦
第一次出现水浒传的位置是:3
在位置2处插入元素后的集合: [红楼梦, 三国演义, 平凡的世界, 西游记, 水浒传]
被删除的元素: 平凡的世界
删除元素后的集合: [红楼梦, 三国演义, 西游记, 水浒传]
被替换的元素是:红楼梦
现在集合中的元素是:[儒林外史, 三国演义, 西游记, 水浒传]
清空集合后:[]
清空集合是否为空:true

与数据库同样,群集中最常用的也是查询的操作,所以这么些片段单独介绍,因为ArrayList完结了RandmoAccess接口,所以与数组相像,每一种成分在ArrayList中都有投机一定的职分,大家得以因而那些角标,也正是索引来获取相应的要素

package collection.list;

import java.util.ArrayList;
import java.util.List;

public class TraverseDemo1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("红楼梦");
        list.add("三国演义");
        list.add("西游记");
        list.add("水浒传");

        String string = null;

        for (int i = 0; i < list.size(); i++) {
            string = list.get(i);
            System.out.println(string);
        }
    }
}

查看API可以见到,ArrayList达成了Iterable接口,调用Iterable接口中的iterator(State of Qatar方法可以回到三个名叫迭代器的接口Iterator,能够把Iterator充作一个游标使用,API很简短,总共就八个办法

boolean hasNext() 
          如果仍有元素可以迭代,则返回 true。 
E next() 
          返回迭代的下一个元素。 
void remove() 
          从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。 

下边包车型大巴代码演示了Iterator的常用用法

package collection.list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TraverseDemo2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("红楼梦");
        list.add("三国演义");
        list.add("西游记");
        list.add("水浒传");

        String string = null;
        Iterator<String> it = list.iterator();

        while (it.hasNext()) {
            string = it.next();
            System.out.println(string);
        }
    }
}

地点的while循环,大家得以改成for循环,这种办法效果说不许高些,因为for循环结束后,iterator变量便释放了

package collection.list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TraverseDemo4 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("红楼梦");
        list.add("三国演义");
        list.add("西游记");
        list.add("水浒传");

        String string = null;

        for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
            string = iterator.next();
            System.out.println(string);
        }
    }
}

经过巩固for循环,也得以遍历ArrayList,巩固for循环的优点是代码简洁,但是不能够获得特定的因素,因为还未脚标了

package collection.list;

import java.util.ArrayList;
import java.util.List;

public class TraverseDemo3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("红楼梦");
        list.add("三国演义");
        list.add("西游记");
        list.add("水浒传");

        for (String string : list) {
            System.out.println(string);
        }
    }
}

让我们看看哪些对给定的整数会集(5,10,0,-1)举行排序:

数量存款和储蓄在ArrayList中

LinkedList

LinkedList也是List接口下二个那多少个首要的达成类,底层数据构造是遵照链表,链表相对于数组(ArrayList的最底层达成)对数据的增加和删除的进程极快,可是询问速度就微微逊色了些,来看一下LinkedList中有意的章程

package collection.list;

import java.util.LinkedList;

import org.junit.Before;
import org.junit.Test;

public class LinkedListDemo {
    LinkedList<String> list = null;

    @Before
    public void init() {
        list = new LinkedList<String>();
    }

    @Test
    public void testOfferFirst() { // 在列表开头插入元素
        list.offerFirst("s1");
        list.offerFirst("s2");
        System.out.println(list); // [s2, s1]
    }

    @Test
    public void testOfferLast() { // 在列表末尾插入元素
        list.offerLast("s1");
        list.offerLast("s2");
        System.out.println(list); // [s1, s2]
    }

    @Test
    public void testPeekFirst() { // 获取但不移除列表的第一个元素;如果列表为空,则返回 null
        list.offerLast("s1");
        list.offerLast("s2");
        list.offerLast("s3");
        System.out.println(list.peekFirst()); // s1
        System.out.println(list); // [s1, s2, s3]
    }

    @Test
    public void testPeekLast() { // 获取但不移除列表的最后一个元素;如果列表为空,则返回 null
        list.offerLast("s1");
        list.offerLast("s2");
        list.offerLast("s3");
        System.out.println(list.peekLast()); // s3
        System.out.println(list); // [s1, s2, s3]
    }

    @Test
    public void testPollFirst() { // 获取并移除列表的第一个元素;如果此列表为空,则返回 null
        list.offerLast("s1");
        list.offerLast("s2");
        list.offerLast("s3");
        System.out.println(list.pollFirst()); // s1
        System.out.println(list); // [s2, s3]
    }

    @Test
    public void testPollLast() { // 获取并移除列表的最后一个元素;如果此列表为空,则返回 null
        list.offerLast("s1");
        list.offerLast("s2");
        list.offerLast("s3");
        System.out.println(list.pollLast()); // s3
        System.out.println(list); // [s1, s2]
    }

}

数据(整数)存储在ArrayList中

private void sortNumbersInArrayList() {
        List<Integer> integers = new ArrayList<>();
        integers.add(5);
        integers.add(10);
        integers.add(0);
        integers.add(-1);
        System.out.println("Original list: " +integers);
        Collections.sort(integers);
        System.out.println("Sorted list: "+integers);
        Collections.sort(integers, Collections.reverseOrder());
        System.out.println("Reversed List: " +integers);
}

输出:

Original list: [5, 10, 0, -1]
Sorted list: [-1, 0, 5, 10]
Reversed List: [10, 5, 0, -1]

private void sortNumbersInArrayList() {

小结

ArrayList:底层数据布局是数组,增删操作慢,查询速度快

LinkedList:底层数据布局是链表,增加和删除操作快,查询速度慢

数据(整数)存储在HashSet中

private void sortNumbersInHashSet() {
        Set<Integer> integers = new HashSet<>();
        integers.add(5);
        integers.add(10);
        integers.add(0);
        integers.add(-1);
        System.out.println("Original set: " +integers);
       // Collections.sort(integers); This throws error since sort method accepts list not collection
        List list = new ArrayList(integers);
        Collections.sort(list);
        System.out.println("Sorted set: "+list);
        Collections.sort(list, Collections.reverseOrder());
        System.out.println("Reversed set: " +list);
 }

输出:

Original set: [0, -1, 5, 10]
Sorted set: [-1, 0, 5, 10]
Reversed set: [10, 5, 0, -1]

在这里个例子中(数据(整数)存款和储蓄在HashSet中),大家看来HashSet被转移为ArrayList实行排序。在不更动为ArrayList的事态下,能够经过应用TreeSet来兑现排序。TreeSet是Set的另三个落到实处,並且在接受私下认可布局函数创造Set时,使用当然排序进行排序。

List integers = new ArrayList<>();

Set

数据(整数)存储在TreeSet中

private void sortNumbersInTreeSet() {
        Set<Integer> integers = new TreeSet<>();
        integers.add(5);
        integers.add(10);
        integers.add(0);
        integers.add(-1);
        System.out.println("Original set: " + integers);
        System.out.println("Sorted set: "+ integers);
        Set<Integer> reversedIntegers = new TreeSet(Collections.reverseOrder());
        reversedIntegers.add(5);
        reversedIntegers.add(10);
        reversedIntegers.add(0);
        reversedIntegers.add(-1);
        System.out.println("Reversed set: " + reversedIntegers);
 }

输出:

Original set: [-1, 0, 5, 10]
Sorted set: [-1, 0, 5, 10]
Reversed set: [10, 5, 0, -1]

在这里种情景下,“Original set:”和“Sorted
set:”两者完全一样,因为大家早就选取了按排序顺序存款和储蓄数据的TreeSet,所以在插入后而不是排序。

到近年来甘休,一切都按时职业,排序就好像是一件稳操胜利的概率的事。以往让大家品尝在逐条Collection中蕴藏自定义对象(举个例子Student),并查阅排序是如何是好事的。

integers.add;

HashSet

HashSet是Set接口的三个最首要的落到实处类,首要的表征是:里面不能够贮存重复成分,况且使用散列的储存方法,所以未有各种,这里的尚未各样指的是存入和抽出的相继不必然一致,HashSet的底部数据结构是哈希表,内部得以完结是依照HashMap

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * default initial capacity (16) and load factor (0.75).
 */
public HashSet() {
    map = new HashMap<>();
}

看个例证(API请自行查阅)

package collection.set;

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo1 {
    public static void main(String[] args) {
        HashSet<String> hs = new HashSet<String>();
        hs.add("s1");
        hs.add("s2");
        hs.add("s2");
        hs.add("s3");

        Iterator<String> it = hs.iterator();

        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

运转结果

s2
s1
s3

从运行结果来看,大家往hashset中存入了多少个成分,当中存了五回的s2,最后只存入了八个,那证明hashset瓜月素确实无法重复,成分的抽取顺序与存放顺序也不平等。越多时候,大家要存放的不止是像字符串那样总结的对象,而是一些自定义对象

package collection.set;

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

现行反革命往hashset中增加自定义对象Student

package collection.set;

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo2 {
    public static void main(String[] args) {
        HashSet<Student> set = new HashSet<Student>();

        set.add(new Student("s1", 21));
        set.add(new Student("s2", 22));
        set.add(new Student("s2", 22));
        set.add(new Student("s3", 23));

        Iterator<Student> it = set.iterator();

        while (it.hasNext()) {
            Student student = it.next();
            System.out.println(student.getName() + ":" + student.getAge());
        }
    }
}

运作结果

s1:21
s2:22
s3:23
s2:22

并发了一个与预期不相同的答案,那正是hashset中加多了多个s2对象,不是说不容许再一次元素存在呢,那又是怎么回事,其实hashset在添法郎素的时候,首先会调用hashCode方法判定会集中是还是不是有对象的哈希码值与新的指标相等,假若不等于,则增加成功,要是相等,那么再持续调用equals方法将那个指标与插入的靶子举办相比,假若为true,则拉长失利,假使为false,则拉长成功,用上边包车型地铁流程图表示越来越直观些

图片 5

那上边的SetDemo1又是怎么回事,别忘了Set德姆o1中增加的是String对象,String类中重写了hashCode与equals方法,上边验证一下所说的那几个结论,重写Student类的hashCode与equals方法

package collection.set;

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int hashCode() {
        System.out.println(this.getName() + "...hashCode...");
        return this.getName().hashCode() + age * 31;
    }

    public boolean equals(Object object) {
        Student student = (Student) object;
        System.out.println(this.name + "...equals..." + student.getName());
        return this.name.equals(student.getName()) && this.age == student.getAge();
    }
}

主程序也改过下

package collection.set;

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo2 {
    public static void main(String[] args) {
        HashSet<Student> hs = new HashSet<Student>();

        boolean isAdd = hs.add(new Student("s1", 21));
        System.out.println(isAdd);
        isAdd = hs.add(new Student("s2", 22));
        System.out.println(isAdd);
        isAdd = hs.add(new Student("s2", 22));
        System.out.println(isAdd);
        isAdd = hs.add(new Student("s3", 23));
        System.out.println(isAdd);

        Iterator<Student> it = hs.iterator();

        while (it.hasNext()) {
            Student student = it.next();
            System.out.println(student.getName() + ":" + student.getAge());
        }
    }
}

运行结果

s1...hashCode...
true
s2...hashCode...
true
s2...hashCode...
s2...equals...s2
false
s3...hashCode...
true
s1:21
s2:22
s3:23

同台来分析一下这么些进度,首先增多s1指标,它调用了hashCode方法,因为刚早先hashset中是还未有其余因素的,所以当时增多成功,为true,接着增多s2目的,形似s2对象也调用hashCode方法,由于s2对象与s1对象的哈希值不肖似,所以hashset以为集合中绝非s2对象,增加成功,接着又增多三个s2对象,依据运转结果,这么些s2对象调用hashCode方法之后,发掘hashset中曾经存在多少个与它哈希值相符的对象(哈希值雷同只可以证实多个对象在内存中的地点一样,对象是不是是同一个还得用equals方法举行推断),所以随着调用了equals方法,由Student类的equals方法知情,这多少个s2对象相近,重临true,所以那时候s2对象增加战败,最终增添s3对象,依据前面包车型地铁分析,轻易领会,s3对象成功的增进到set会集中了,最后hashset加多了s1,s2,s3多少个对象。

通常hashset操作的对象,都亟需重写hashCode与equals方法。

数据(Student对象)存储在ArrayList中

private void sortStudentInArrayList() {
        List<Student> students = new ArrayList<>();
        Student student1 = createStudent("Biplab", 3);
        students.add(student1);
        Student student2 = createStudent("John", 1);
        students.add(student2);
        Student student3 =  createStudent("Pal", 5);
        students.add(student3);
        Student student4 = createStudent("Biplab", 2);
        students.add(student4);
        System.out.println("Original students list: " + students);
        Collections.sort(students);// Error here
        System.out.println("Sorted students list: " + students);
        Collections.sort(students, Collections.reverseOrder());
        System.out.println("Reversed students list: " + students);
}
private Student createStudent(String name, int no) {
        Student student = new Student();
        student.setName(name);
        student.setNo(no);
        return student;
}
public class Student {
    String name;
    int no;
    public String getName() {
        return name;
    }
    public int getNo() {
        return no;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setNo(int no) {
        this.no = no;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", no=" + no +
                '}';
    }
}

那会抛出编译时不当,并展现以下错误音讯:

sort(java.util.List<T>)
in Collections cannot be applied
to (java.util.List<com.example.demo.dto.Student>)
reason: no instance(s) of type variable(s) T exist so that Student 
conforms to Comparable<? Super T>

为了减轻这些主题素材,要么Student类须要完结Comparable,要么须求在调用Collections.sort时传递Comparator对象。在整型情状下,排序方法未有不当,因为Integer类完毕了Comparable。让大家看看,完毕Comparable或传递Comparator怎样解决那几个标题,以至排序方法怎么样兑现Collection排序。

integers.add;

TreeSet

地方说起的HashSet,当大家获取成分的时候,由于底层数据构造是哈希表,所以输出的依次是冬日的,假诺急需对Set会集中的成分进行排序,那就要用到set中另一个生死攸关的落到实处类TreeSet,TreeSet的尾部数据构造是二叉树,能够对Set会集中的成分举行排序,先来看个例子

package collection.set;

import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo1 {
    public static void main(String[] args) {
        TreeSet<String> ts = new TreeSet<String>();
        ts.add("abc");
        ts.add("aaa");
        ts.add("acd");
        ts.add("abcd");

        Iterator<String> it = ts.iterator();

        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

出口结果

aaa
abc
abcd
acd

输出的结果与存入的次第不类似,那么TreeSet集结内部是依据什么来排序的啊,查阅API知道,有二种方法得以兑现对成分的排序

  1. 利用要素的本来顺序对成分实行排序
  2. 基于创建 set 时提供的 Comparator
    举办排序

那边使用的正是率先种,依照成分的本来顺序进行排序,接下去我们往TreeSet集合中增添自定义对象Student看看

package collection.set;

import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo2 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<Student>();
        ts.add(new Student("s01", 20));
        ts.add(new Student("s02", 22));
        ts.add(new Student("s04", 21));
        ts.add(new Student("s03", 22));

        Iterator<Student> it = ts.iterator();

        while (it.hasNext()) {
            Student student = it.next();
            System.out.println(student.getName() + " : " + student.getAge());
        }
    }
}

此刻,调控台抛出了如下至极新闻

Exception in thread "main" java.lang.ClassCastException: collection.set.Student cannot be cast to java.lang.Comparable
    at java.util.TreeMap.compare(Unknown Source)
    at java.util.TreeMap.put(Unknown Source)
    at java.util.TreeSet.add(Unknown Source)
    at collection.set.TreeSetDemo2.main(TreeSetDemo2.java:9)

遵照提醒音信,是因为类型转换错误引起的,这里现身了二个Comparable,通过查询API获知Comparable是八个接口,这一个接口非常的粗略,独有八个措施,该办法在累计目的时隐式地被调用

int compareTo(T o)

    相比较此目的与钦点对象的次第。假诺该对象小于、等于或超过钦点对象,则分别重返负整数、零或正整数。

鉴于TreeSet会对存入的对象举行排序,所以供给对象之间相互进行相比较,也便是怀有相比性,那怎么着手艺让对象具有比较性呢,Java中提供了三个接口Comparable,只要达成了那个接口,对象时期就有所了比较性,提起此地,应该能够推断出来为何存入String对象的时候能够成功了,今后大家让Student对象完成Comparable接口,让它富有相比性

package collection.set;

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Student s) {
        return this.age - s.age; // 按照年龄升序排列
    }
}

代码的运行结果

s01 : 20
s04 : 21
s02 : 22

主题材料来了,存了四个对象,怎么就输出多个目的了,重新纪念一下compareTo方法,因为compareTo方法中是依靠学子的年龄大小举办相比较,而s02与s03这些学子的年龄相近,所以感觉他俩是同二个对象,这里假定姓名与年纪都无差别的时候,以为七个学子才是同一位,改进一下compareTo方法

@Override
public int compareTo(Student s) {
    int num = this.age - s.age;

    if (num == 0) { // 当年龄相同时,按照姓名排序
        return this.getName().compareTo(s.getName());
    }

    return num; // 按照年龄升序排列
}

这儿的运维结果就符合大家的预料了

s01 : 20
s04 : 21
s02 : 22
s03 : 22

来思量三个难点,假若明天优先遵照学子的年龄进行排序,这如何是好,首先想到更改我们的代码,那是能够的,这假设蓦地有一天又要求基于年龄的长短进行排序,那难道又要校勘我们的代码吗,别忘了,还应该有另一种格局能够完毕对目的的排序,那正是

听大人说成立 set 时提供的 Comparator 举办排序

什么看头啊,看下TreeSet的结构函数就领会了

TreeSet(Comparator<? super E> comparator)
            布局一个新的空 TreeSet,它依照内定比较器进行排序。

这边在开头化TreeSet的时候,传入了二个参数Comparator,Comparator也是一个接口,此中compare方法能够用来相比对象

int compare(T o1, T o2)

    相比较用来排序的多少个参数。遵照第一个参数小于、等于或高于第4个参数分别再次回到负整数、零或正整数。

前段时间新建多少个类达成Comparator接口

package collection.set;

import java.util.Comparator;

public class StudentComparator implements Comparator<Student> {

    @Override
    public int compare(Student s1, Student s2) {
        int num = s1.getName().compareTo(s2.getName());

        if (num == 0) {
            return s1.getAge() - s2.getAge();
        }

        return num;
    }

}

在构造TreeSet的时候,传入StudentComparator

package collection.set;

import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo3 {
    public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<Student>(new StudentComparator());
        ts.add(new Student("s01", 20));
        ts.add(new Student("s02", 22));
        ts.add(new Student("s04", 21));
        ts.add(new Student("s03", 22));

        Iterator<Student> it = ts.iterator();

        while (it.hasNext()) {
            Student student = it.next();
            System.out.println(student.getName() + " : " + student.getAge());
        }
    }
}

运作结果契合大家的渴求

s01 : 20
s02 : 22
s03 : 22
s04 : 21

别忘了,那个时候Student类仍是促成了Comparable接口的,内部有谈得来的相比法规,从地方的运转结果能够得出三个结论构造器中盛传的相比较器准绳优于对象自己的比较准则,末代如若急需依据其他方式实行排序,则只需新建多少个比较器就能够,就不必修正Student类了。

使用Comparable排序

package com.example.demo.dto;
public class Student implements Comparable{
    String name;
    int no;
    public String getName() {
        return name;
    }
    public int getNo() {
        return no;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setNo(int no) {
        this.no = no;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", no=" + no +
                '}';
    }
    @Override
    public int compareTo(Object o) {
         return this.getName().compareTo(((Student) o).getName());
    }
}

输出:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]
Reversed students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]

在全部示例中,为了颠倒顺序,大家接纳“Collections.sort(students,
Collections.reverseOrder(卡塔尔”,相反的,它能够由此转移compareTo(..State of Qatar方法的达成而达到目的,且compareTo(…)的贯彻看起来像那样 :

@Override
  public int compareTo(Object o) {
     return (((Student) o).getName()).compareTo(this.getName());
  }

输出:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]
Reversed students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]

借使大家重点输出结果,大家能够看出“Sorted students
list:”以颠倒的相继(按学生name)输出学生音讯。

到近期截至,对学员的排序是依据学子的“name”而非“no”来产生的。如若大家想按“no”排序,大家只必要更换Student类的compareTo(Object
o卡塔尔国完毕,如下所示:

 @Override
 public int compareTo(Object o) {
    return  (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1));
}

输出:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='John', no=1}, Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='Pal', no=5}]
Reversed students list: [Student{name='Pal', no=5}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}]

在地方的输出中,大家得以看出“no”2和3的两名学员具有同样的名字“Biplab”。

以后假若我们首先须要按“name”对那几个学员开展排序,如若高出1名上学的儿童具备同等姓名的话,则那些学员须要按“no”排序。为了得以达成那一点,大家供给退换compareTo(…卡塔尔方法的兑现,如下所示:

@Override
  public int compareTo(Object o) {
    int result = this.getName().compareTo(((Student) o).getName());
        if(result == 0) {
            result = (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1));
        }
        return  result;
 }

输出:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}]
Reversed students list: [Student{name='Pal', no=5}, Student{name='John', no=1}, Student{name='Biplab', no=3}, Student{name='Biplab', no=2}]

integers.add;

小结

HashSet:底层数据构造是哈希表,成分冬日,往HashSet里面存的自定义成分要复写hashCode和equals方法,以承保成分的独一性,保障元素独一性的逐条

  1. 调用成分的hashCode方法,假如分裂,则存入成分,若是雷同,转2
  2. 调用成分的equals方法,假如分化,则存入元素,假若一致,则不存

只顾:hashCode和equals方法在联谊操作成分时私下认可被调用

TreeSet:底层数据构造式二叉树,成分有序,能够对聚聚集的成分举行排序,有二种方法

  1. 要素达成Compareble接口,覆盖compareTo方法
  2. 自定义二个类,实现Comparator接口,覆盖compare方法

使用Comparator排序

为了遵照“name”对Students实行排序,大家将助长三个Comparator并将其传递给排序方法:

public class Sorting {
 private void sortStudentInArrayList() {
        List<Student> students = new ArrayList<>();
        Student student1 = createStudent("Biplab", 3);
        students.add(student1);
        Student student2 = createStudent("John", 1);
        students.add(student2);
        Student student3 =  createStudent("Pal", 5);
        students.add(student3);
        Student student4 = createStudent("Biplab", 2);
        students.add(student4);
        System.out.println("Original students list: " + students);
        Collections.sort(integers, new NameComparator());
        System.out.println("Sorted students list: " + students);
 }
}
public class Student {
    String name;
    int no;
    public String getName() {
        return name;
    }
    public int getNo() {
        return no;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setNo(int no) {
        this.no = no;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", no=" + no +
                '}';
    }
}
class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareTo(o2.getName());
    }
}

输出:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=3}, Student{name='Biplab', no=2}, Student{name='John', no=1}, Student{name='Pal', no=5}]

同一,假若我们想家有家规“no”对Students排序,那么能够再加多二个Comparator(NoComparator.java),并将其传递给排序方法,然后数据将按“no”排序。

这两天,借使大家想透过“name”然后“no”对学员进行排序,那么能够在compare(…卡塔尔国内结合三种逻辑来完毕。

class NameNoComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        int result = o1.getName().compareTo(o2.getName());
        if(result == 0) {
            result =  o1.getNo() <  o2.getNo() ? -1 : o1.getNo() == o2.getNo() ? 0 : 1;
        }
       return result;
    }
}

输出:

Original students list: [Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}, Student{name='Biplab', no=2}]
Sorted students list: [Student{name='Biplab', no=2}, Student{name='Biplab', no=3}, Student{name='John', no=1}, Student{name='Pal', no=5}]

integers.add;

Map

Map那么些连串下常常接收的最首要有HashMap和TreeMap这七个完成类,能够与set体系中的HashSet和TreeSet进行对照学习,前边提到Set的最底层正是用Map,所以她们之间有个别许相似,比方他们中的成分都以冬季的。map在这里地是炫目的意思,跟数学中集聚的璀璨概念是五个意味,map中的成分是以键值没有错款型积攒的,四个键一点钟情二个值,何况需求有限支撑键的独一性,不然值将会被掩盖。

数据(Students对象)存储在Set中

在Set的那些情景下,大家供给将HashSet调换为ArrayList,或接收TreeSet对数据进行排序。其它,大家清楚要使Set职业,equals(…卡塔尔和hashCode(State of Qatar方法供给被遮住。上面是基于“no”字段覆盖equals和hashcode的例证,且这一个是IDE自动生成的代码。与Comparable或Comparator相关的此外轮代理公司码与ArrayList相似。

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return no == student.no;
    }
    @Override
    public int hashCode() {
        return Objects.hash(no);
    }

System.out.println(“Original list: ” +integers);

HashMap

HashMap与HashSet同样,底层数据布局基于哈希表,允许存在null的键,null的值。

package map;

import java.util.Collection;
import java.util.HashMap;

public class HashMapDemo1 {
    public static void main(String[] args) {
        HashMap<String, Integer> hm = new HashMap<String, Integer>();
        hm.put("s1", 21); // 添加元素
        hm.put("s2", 22);
        hm.put("s3", 23);
        hm.put("s4", 24);
        hm.put(null, null); // 存入null键,null值

        Collection<Integer> values = hm.values(); // 获取集合中所有的值
        System.out.println(values);
        System.out.println(hm.get("s1")); // 根据key获取对应的值
        System.out.println(hm.containsKey("s1")); // 判断是否包含给定的键
        System.out.println(hm.isEmpty()); // 集合是否为空
        System.out.println(hm.size()); // 集合中包含的键值对个数
        System.out.println(hm.remove("s2")); // 移除集合中给定键所对应的值,并返回该值
        System.out.println(hm.size()); // 集合中包含的键值对个数
        values = hm.values(); // 获取集合中所有的值
        System.out.println(values);
    }
}

运作结果

[null, 22, 21, 23, 24]
21
true
false
5
22
4
[null, 21, 23, 24]

常备,大家需求成没有错抽出会集中的键值对,那么依据前边的涉世,查找一下,开掘并从未像样ArrayList,HashSet中所使用的迭代器,那么用如何方法能够获得键值对吧,Map集结中二种办法能够实现

  1. 行使keySet(State of Qatar方法先将Map调换到Set,然后用Set中的迭代器举办迭代
  2. 应用entrySet(卡塔尔国方法,将Map转成具备映射关系的Set视图

先看率先种

package map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class HashMapDemo2 {
    public static void main(String[] args) {
        HashMap<String, Integer> hm = new HashMap<String, Integer>();
        hm.put("s1", 21);
        hm.put("s2", 22);
        hm.put("s3", 23);
        hm.put("s4", 24);

        Set<String> keySet = hm.keySet();

        Iterator<String> it = keySet.iterator();

        while (it.hasNext()) {
            String key = (String) it.next();
            Integer value = hm.get(key);

            System.out.println("key=" + key + "," + "value=" + value);
        }

    }
}

再来看第三种,也正是选用entrySet方法

package map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapDemo3 {
    public static void main(String[] args) {
        HashMap<String, Integer> hm = new HashMap<String, Integer>();
        hm.put("s1", 21);
        hm.put("s2", 22);
        hm.put("s3", 23);
        hm.put("s4", 24);

        Set<Map.Entry<String, Integer>> entry = hm.entrySet();

        Iterator<Map.Entry<String, Integer>> it = entry.iterator();

        while (it.hasNext()) {
            Map.Entry<String, Integer> me = it.next();
            String key = me.getKey();
            Integer value = me.getValue();
            System.out.println("key=" + key + "," + "value=" + value);
        }

    }
}

数码(Students对象)存款和储蓄在数组中

为了对数组排序,我们必要做与排序ArrayList相像的业务(要么实行Comparable要么传递Comparable给sort方法)。在这里种景色下,sort方法是“Arrays.sort(Object[]
a )”而非“Collections.sort(..)”。

private void sortStudentInArray() {
       Student [] students = new Student[4];
        Student student1 = createStudent("Biplab", 3);
        students[0] = student1;
        Student student2 = createStudent("John", 1);
        students[1] = student2;
        Student student3 =  createStudent("Pal", 5);
        students[2] = student3;
        Student student4 = createStudent("Biplab", 2);
        students[3] = student4;
        System.out.print("Original students list: ");
        for (Student student: students) {
            System.out.print( student + " ,");
        }
        Arrays.sort(students);
        System.out.print("nSorted students list: ");
        for (Student student: students) {
            System.out.print( student +" ,");
        }
        Arrays.sort(students, Collections.reverseOrder());
        System.out.print("nReversed students list: " );
        for (Student student: students) {
            System.out.print( student +" ,");
        }
   }
//Student class
// All properties goes here
  @Override
   public int compareTo(Object o) {
       int result =this.getName().compareTo(((Student)o).getName());
       if(result ==0) {
           result =  (this.getNo() < ((Student) o).getNo() ? -1 : (this.getNo() == ((Student) o).getNo() ? 0 : 1));
       }
       return  result;
   }

输出:

Original students list: Student{name='Biplab', no=3} ,Student{name='John', no=1} ,Student{name='Pal', no=5} ,Student{name='Biplab', no=2} ,
Sorted students list: Student{name='Biplab', no=2} ,Student{name='Biplab', no=3} ,Student{name='John', no=1} ,Student{name='Pal', no=5} ,
Reversed students list: Student{name='Pal', no=5} ,Student{name='John', no=1} ,Student{name='Biplab', no=3} ,Student{name='Biplab', no=2} ,

Collections.sort;

TreeMap

有了前头学习TreeSet的根基,学习TreeMap就轻巧多了,与TreeSet同样,TreeMap底层数据构造也是基于二叉树,能够对Map集合中的因素进行排序,相似有二种情势

  1. 依照键的自然顺序举行排序
  2. 依照创设映射时提供的 Comparator 进行排序

具体的选取办法可以参照上述所讲的TreeSet,本小节的总计见最终。

结论

我们平日对Comparable/Comparator的施用以至曾几何时使用哪个感觉蒙在鼓里。下边是本人总括的Comparable/Comparator的行使处境。

Comparator:

  • 当大家想排序一个不能够更改的类的实例时。比方来自jar的类的实例。
  • 听新闻说用例供给排序不相同的字段时,举例,三个用例须求通过“name”排序,还也许有个想要遵照“no”排序,可能部分用例需要经过“name和no”来排序。

Comparable:

应当在定义类时知道排序的各类时利用,何况不会有其它任何索要运用Collection
/数组来依据此外字段排序的动静。

留意:笔者还未介绍Set /
List的底细。笔者假如读者已经驾驭了那个内容。别的,未有提供使用Set /
array实行排序的事无巨细示例,因为达成与ArrayList极度相符,而ArrayList笔者曾经详细给出了演示。

末尾,感激阅读。

System.out.println(“Sorted list: “+integers);

群集工具类

Collections.sort(integers, Collections.reverseOrder;

Collections

Collections是集聚框架中的二个工具类,该类中提供了一各样静态的艺术来操作集合,包罗对list集结的排序,二分查找,查询集结的最大最小值等,像上面介绍的可比常用的ArrayList,HashSet,TreeMap等联谊都是线程不一同的,当有七十四线程操作这几个聚焦的时候,能够应用该工具类synchronizedList,synchronizedSet,synchronizedMap等艺术将线程差别步的集聚转成同步的。

package utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CollectionsDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("abcd");
        list.add("aaa");
        list.add("zz");
        list.add("y");
        list.add("kkkkk");

        // 使用二分查找指定元素,找到则返回索引
        int index = Collections.binarySearch(list, "zz");
        System.out.println("111 : " + index);

        // 没有找到,返回(-(插入点) - 1)
        index = Collections.binarySearch(list, "zzz");
        System.out.println("222 : " + index);

        // 查找列表最大值
        String max = Collections.max(list);
        System.out.println("333 : " + max);

        // 根据元素的自然顺序对列表按升序进行排序
        Collections.sort(list);
        System.out.println("444 : " + list);

        // 根据给定比较器对指定列表进行排序
        Collections.sort(list, new StrLenComparator());
        System.out.println("555 : " + list);

        // reverseOrder()强行逆转实现了 Comparable 接口的对象 collection 的自然顺序
        Collections.sort(list, Collections.reverseOrder());
        System.out.println("666 : " + list);

        // 反转列表中元素的顺序
        Collections.reverse(list);
        System.out.println("777 : " + list);

        // 随机排列列表的元素
        Collections.shuffle(list);
        System.out.println("888 : " + list);

        // 返回一个空的集合,该集合不可变,如果对该集合进行增删操作,会抛出java.lang.UnsupportedOperationException异常
        List<String> emptyList = Collections.<String> emptyList();
        System.out.println("999 : " + emptyList);

        emptyList = Collections.EMPTY_LIST;
        System.out.println("000 : " + emptyList);
    }
}

class StrLenComparator implements Comparator<String> {

    @Override
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
}

运维结果

111 : 2
222 : -6
333 : zz
444 : [aaa, abcd, kkkkk, y, zz]
555 : [y, zz, aaa, abcd, kkkkk]
666 : [zz, y, kkkkk, abcd, aaa]
777 : [aaa, abcd, kkkkk, y, zz]
888 : [kkkkk, abcd, y, aaa, zz]
999 : []
000 : []

System.out.println(“Reversed List: ” +integers);

Arrays

Arrays富含用来操作数组(举个例子排序和查找)的各类措施,相同里面也都以静态方法。若是内定数组引用为
null,则此类中的方法都会抛出 NullPointerException。

package utils;

import java.util.Arrays;
import java.util.List;

public class ArraysDemo {
    public static void main(String[] args) {
        String[] array = { "abc", "ggg", "dh", "zzzz", "o" };

        // 数组转集合,如果试图对集合进行增删操作,则会抛出UnsupportedOprationException异常
        List<String> list = Arrays.asList(array);
        System.out.println("111 : " + list);

        // 数组转字符串
        String string = Arrays.toString(array);
        System.out.println("222 : " + string);

        // 使用二分查找指定元素,找到则返回索引
        int index = Arrays.binarySearch(array, "ccc");
        System.out.println("333 : " + index);

        // 复制数组
        String[] newArray = Arrays.copyOf(array, array.length);
        System.out.println("444 : " + Arrays.asList(newArray));

        // 复制指定范围的数组
        String[] newArrayRange = Arrays.copyOfRange(array, 0, 2);
        System.out.println("555 : " + Arrays.asList(newArrayRange));

        // 判断两个数组是否相等
        boolean equals = Arrays.equals(array, newArray);
        System.out.println("666 : " + equals);

        // 对数组元素进行排序
        Arrays.sort(array);
        System.out.println("777 : " + Arrays.asList(array));

        // 转成字符串
        String arrayString = Arrays.toString(array);
        System.out.println("888 : " + arrayString);

        // 用指定的值替换数组中的每个值
        Arrays.fill(array, "new");
        System.out.println("999 : " + Arrays.asList(array));
    }
}

运营结果

111 : [abc, ggg, dh, zzzz, o]
222 : [abc, ggg, dh, zzzz, o]
333 : -2
444 : [abc, ggg, dh, zzzz, o]
555 : [abc, ggg]
666 : true
777 : [abc, dh, ggg, o, zzzz]
888 : [abc, dh, ggg, o, zzzz]
999 : [new, new, new, new, new]

}

总结

汇合能够积累区别类型的目的,何况长度可变

List集合:成分有序,可重复,可存款和储蓄null成分,倘诺想要成分不另行,则存入对象急需重写equals方法

  ArrayList:底层数据布局是数组,查询快

  LinkedList:底层数据结构是链表,增加和删除操作快

Set集合:成分冬天,不可重复,可存款和储蓄null成分

  HashSet:底层数据构造是哈希表,存取速度快,成分独一、线程区别步

  TreeSet:底层数据布局式二叉树,能够对成分实行排序,成分有序、线程不一致步

Mpa集合:集结存款和储蓄的是键值对,键需求保证唯一,不然值将会被覆盖

  HashMap:底层数据布局是哈希表,允许利用null键和null值,线程区别步,不过成效高

  TreeMap:底层数据构造是二叉树,允许利用null键和null值,线程差别步,可以对成分举行排序

工具类:Collections是集聚框架中的贰个工具类,该类提供了一层层静态的办法来操作集结,而Collection是集合框架的三个顶层接口,二者直接或直接的关联。

输出:

Original list: [5, 10, 0, -1]

Sorted list: [-1, 0, 5, 10]

Reversed List: [10, 5, 0, -1]

数量存款和储蓄在HashSet中

private void sortNumbersInHashSet() {

Set integers = new HashSet<>();

integers.add;

integers.add;

integers.add;

integers.add;

System.out.println(“Original set: ” +integers); // Collections.sort;
This throws error since sort method accepts list not collection

List list = new ArrayList;

Collections.sort;

System.out.println(“Sorted set: “+list);

Collections.sort(list, Collections.reverseOrder;

System.out.println(“Reversed set: ” +list);

}

输出:

Original set: [0, -1, 5, 10]Sorted set: [-1, 0, 5, 10]Reversed set:
[10, 5, 0, -1]

在此个例子中存放在HashSet中),大家看见HashSet被撤换为ArrayList举办排序。在不转换为ArrayList的情事下,能够由此接受TreeSet来促成排序。TreeSet是Set的另三个完结,並且在行使暗中同意构造函数创造Set时,使用当然排序举办排序。

数码存款和储蓄在TreeSet中

private void sortNumbersInTreeSet() {

Set integers = new TreeSet<>();

integers.add;

integers.add;

integers.add;

integers.add;

System.out.println(“Original set: ” + integers);

System.out.println(“Sorted set: “+ integers);

Set reversedIntegers = new TreeSet(Collections.reverseOrder;

reversedIntegers.add;

reversedIntegers.add;

reversedIntegers.add;

reversedIntegers.add;

System.out.println(“Reversed set: ” + reversedIntegers);

}

输出:

Original set: [-1, 0, 5, 10]Sorted set: [-1, 0, 5, 10]Reversed set:
[10, 5, 0, -1]

在此种情景下,“Original set:”和“Sorted
set:”两个大同小异,因为我们早就使用了按排序顺序存款和储蓄数据的TreeSet,所以在插入后不要排序。

到近日结束,一切都准时专门的学业,排序犹如是一件轻松的事。以往让我们尝试在依次Collection中存放自定义对象(比如Student),并查阅排序是如何工作的。

数据(Student对象)存储在ArrayList中

private void sortStudentInArrayList() {

List students = new ArrayList<>();

Student student1 = createStudent(“Biplab”, 3);

students.add;

Student student2 = createStudent(“John”, 1);

students.add;

Student student3 = createStudent;

students.add;

Student student4 = createStudent(“Biplab”, 2);

students.add;

System.out.println(“Original students list: ” + students);

Collections.sort;// Error here

System.out.println(“Sorted students list: ” + students);

Collections.sort(students, Collections.reverseOrder;

System.out.println(“Reversed students list: ” + students);

}private Student createStudent(String name, int no) {

Student student = new Student();

student.setName;

student.setNo; return student;

}public class Student {

String name; int no; public String getName() { return name;

} public int getNo() { return no;

} public void setName(String name) { this.name = name;

} public void setNo { this.no = no;

}

@Override public String toString() { return “Student{” + “name='” + name

  • ”’ + “, no=” + no + ‘}’;

}

}

那会抛出编写翻译时不当,并显示以下错误音信:

sort(java.util.List<T>)in Collections cannot be appliedto
(java.util.List<com.example.demo.dto.Student>)reason: no instance
of type variable T exist so that Student conforms to Comparable<?
Super T>

为了杀绝那一个标题,要么Student类必要完成Comparable,要么供给在调用Collections.sort时传递Comparator对象。在整型情状下,排序方法对的误,因为Integer类完结了Comparable。让我们看看,完结Comparable或传递Comparator怎么样缓和这么些难点,以至排序方法如何落到实处Collection排序。

使用Comparable排序

package com.example.demo.dto;public class Student implements Comparable{

String name; int no; public String getName() { return name;

} public int getNo() { return no;

} public void setName(String name) { this.name = name;

} public void setNo { this.no = no;

} @Override

public String toString() { return “Student{” + “name='” + name + ”’ +
“, no=” + no + ‘}’;

} @Override

public int compareTo { return this.getName().compareTo( o).getName;

}

}

输出:

Original students list: [Student{name=’Biplab’, no=3},
Student{name=’John’, no=1}, Student{name=’Pal’, no=5},
Student{name=’Biplab’, no=2}]

Sorted students list: [Student{name=’Biplab’, no=3},
Student{name=’Biplab’, no=2}, Student{name=’John’, no=1},
Student{name=’Pal’, no=5}]

Reversed students list: [Student{name=’Pal’, no=5},
Student{name=’John’, no=1}, Student{name=’Biplab’, no=3},
Student{name=’Biplab’, no=2}]

在具备示例中,为了颠倒顺序,大家选拔“Collections.sort(students,
Collections.reverseOrder()”,相反的,它能够通过退换compareTo方法的兑现而实现目的,且compareTo
的得以达成看起来像那样 :

@Override

public int compareTo { return ( o).getName.compareTo(this.getName;

}

输出:

Original students list: [Student{name=’Biplab’, no=3},
Student{name=’John’, no=1}, Student{name=’Pal’, no=5},
Student{name=’Biplab’, no=2}]

Sorted students list: [Student{name=’Pal’, no=5}, Student{name=’John’,
no=1}, Student{name=’Biplab’, no=3}, Student{name=’Biplab’, no=2}]

Reversed students list: [Student{name=’Biplab’, no=3},
Student{name=’Biplab’, no=2}, Student{name=’John’, no=1},
Student{name=’Pal’, no=5}]

一旦大家观看输出结果,大家得以看出“Sorted students
list:”以颠倒的相继输出学子音信。

到近来截止,对学子的排序是借助学子的“name”而非“no”来形成的。若是大家想按“no”排序,咱们只要求改换Student类的compareTo达成,如下所示:

@Override

public int compareTo { return (this.getNo() < o).getNo() ? -1 :
(this.getNo() == o).getNo() ? 0 : 1));

}

输出:

Original students list: [Student{name=’Biplab’, no=3},
Student{name=’John’, no=1}, Student{name=’Pal’, no=5},
Student{name=’Biplab’, no=2}]

Sorted students list: [Student{name=’John’, no=1},
Student{name=’Biplab’, no=2}, Student{name=’Biplab’, no=3},
Student{name=’Pal’, no=5}]

Reversed students list: [Student{name=’Pal’, no=5},
Student{name=’Biplab’, no=3}, Student{name=’Biplab’, no=2},
Student{name=’John’, no=1}]

在上头的出口中,我们得以观望“no”2和3的两名学子具备相同的名字“Biplab”。

当今只要我们率先供给按“name”对这个学子实行排序,假若超越1名学员具备同样姓名的话,则那个学子必要按“no”排序。为了促成那或多或少,大家要求更改compareTo方法的得以达成,如下所示:

@Override

public int compareTo { int result = this.getName().compareTo(
o).getName; if(result == 0) {

result = (this.getNo() < o).getNo() ? -1 : (this.getNo() ==
o).getNo() ? 0 : 1));

} return result;

}

输出:

Original students list: [Student{name=’Biplab’, no=3},
Student{name=’John’, no=1}, Student{name=’Pal’, no=5},
Student{name=’Biplab’, no=2}]

Sorted students list: [Student{name=’Biplab’, no=2},
Student{name=’Biplab’, no=3}, Student{name=’John’, no=1},
Student{name=’Pal’, no=5}]

Reversed students list: [Student{name=’Pal’, no=5},
Student{name=’John’, no=1}, Student{name=’Biplab’, no=3},
Student{name=’Biplab’, no=2}]

使用Comparator排序

为了根据“name”对Students实行排序,大家将增多二个Comparator并将其传递给排序方法:

public class Sorting { private void sortStudentInArrayList() {

List students = new ArrayList<>();

Student student1 = createStudent(“Biplab”, 3);

students.add;

Student student2 = createStudent(“John”, 1);

students.add;

Student student3 = createStudent;

students.add;

Student student4 = createStudent(“Biplab”, 2);

students.add;

System.out.println(“Original students list: ” + students);

Collections.sort(integers, new NameComparator;

System.out.println(“Sorted students list: ” + students);

}

}public class Student {

String name; int no; public String getName() { return name;

} public int getNo() { return no;

} public void setName(String name) { this.name = name;

} public void setNo { this.no = no;

} @Override

public String toString() { return “Student{” + “name='” + name + ”’ +
“, no=” + no + ‘}’;

}

}class NameComparator implements Comparator { @Override

public int compare(Student o1, Student o2) { return
o1.getName().compareTo(o2.getName;

}

}

输出:

Original students list: [Student{name=’Biplab’, no=3},
Student{name=’John’, no=1}, Student{name=’Pal’, no=5},
Student{name=’Biplab’, no=2}]

Sorted students list: [Student{name=’Biplab’, no=3},
Student{name=’Biplab’, no=2}, Student{name=’John’, no=1},
Student{name=’Pal’, no=5}]

一致,即使大家想鲁人持竿“no”对Students排序,那么能够再增加八个Comparator(NoComparator.java),并将其传递给排序方法,然后数据将按“no”排序。

昨天,如果大家想通过“name”然后“no”对学子开展排序,那么能够在compare内组成二种逻辑来促成。

class NameNoComparator implements Comparator { @Override

public int compare(Student o1, Student o2) { int result =
o1.getName().compareTo(o2.getName; if(result == 0) {

result = o1.getNo() < o2.getNo() ? -1 : o1.getNo() == o2.getNo() ? 0
: 1;

} return result;

}

}

输出:

Original students list: [Student{name=’Biplab’, no=3},
Student{name=’John’, no=1}, Student{name=’Pal’, no=5},
Student{name=’Biplab’, no=2}]

Sorted students list: [Student{name=’Biplab’, no=2},
Student{name=’Biplab’, no=3}, Student{name=’John’, no=1},
Student{name=’Pal’, no=5}]

数据(Students对象)存储在Set中

在Set的那个场馆下,大家必要将HashSet转变为ArrayList,或使用TreeSet对数码进行排序。别的,大家驾驭要使Set工作,equals和hashCode(State of Qatar方法要求被覆盖。下边是基于“no”字段覆盖equals和hashcode的事例,且那个是IDE自动生成的代码。与Comparable或Comparator相关的其它轮代理公司码与ArrayList形似。

@Override

public boolean equals { if (this == o) return true; if (o == null ||
getClass() != o.getClass return false;

Student student = o; return no == student.no;

} @Override

public int hashCode() { return Objects.hash;

}

数量(Students对象)存款和储蓄在数组中

为了对数组排序,大家需求做与排序ArrayList相近的工作(要么施行Comparable要么传递Comparable给sort方法)。在这里种情状下,sort方法是“Arrays.sort(Object[]
a )”而非“Collections.sort”。

private void sortStudentInArray() {

Student [] students = new Student[4];

Student student1 = createStudent(“Biplab”, 3);

students[0] = student1;

Student student2 = createStudent(“John”, 1);

students[1] = student2;

Student student3 = createStudent;

students[2] = student3;

Student student4 = createStudent(“Biplab”, 2);

students[3] = student4;

System.out.print(“Original students list: “); for (Student student:
students) {

System.out.print( student + ” ,”);

}

Arrays.sort;

System.out.print(“nSorted students list: “); for (Student student:
students) {

System.out.print( student +” ,”);

}

Arrays.sort(students, Collections.reverseOrder;

System.out.print(“nReversed students list: ” ); for (Student student:
students) {

System.out.print( student +” ,”);

}

}//Student class// All properties goes here

@Override public int compareTo { int result
=this.getName().compareTo(o).getName; if(result ==0) {

result = (this.getNo() < o).getNo() ? -1 : (this.getNo() ==
o).getNo() ? 0 : 1));

} return result;

}

输出:

Original students list: Student{name=’Biplab’, no=3}
,Student{name=’John’, no=1} ,Student{name=’Pal’, no=5}
,Student{name=’Biplab’, no=2} ,

Sorted students list: Student{name=’Biplab’, no=2}
,Student{name=’Biplab’, no=3} ,Student{name=’John’, no=1}
,Student{name=’Pal’, no=5} ,

Reversed students list: Student{name=’Pal’, no=5} ,Student{name=’John’,
no=1} ,Student{name=’Biplab’, no=3} ,Student{name=’Biplab’, no=2} ,

结论

笔者们常常对Comparable/Comparator的行使以至曾几何时使用哪个感觉纠缠。下边是本身计算的Comparable/Comparator的使用处境。

Comparator:

当大家想排序一个不可能更改的类的实例时。比方来自jar的类的实例。

依赖用例供给排序分歧的字段时,举个例子,一个用例需求通过“name”排序,还应该有个想要依据“no”排序,大概有些用例要求经过“name和no”来排序。

Comparable:

有道是在定义类时知道排序的依次时采纳,而且不会有其余任何索要利用Collection
/数组来依照别的字段排序的景观。

潜心:作者平昔不介绍Set /
List的内部原因。作者只要读者已经领悟了那几个内容。此外,未有提供使用Set /
array实行排序的详尽示例,因为达成与ArrayList非常近似,而ArrayList作者早已详细给出了演示。

笔者整理了一部分java进级学习资料和面试题,须求材料的请加JAVA高阶学习Q群:664389243
那是小编创设的java高阶学习调换群,加群一齐交换学习深造。群里也可以有笔者收拾的今年前卫最全的java高阶学习材质!

发表评论

电子邮件地址不会被公开。 必填项已用*标注