去重——List如何去(简单比较、重写Equals()、实现Comparable接口的排序)

简单类型的去重

代码示例

 List list = Arrays.asList(1,232,3,2,4,3,534,57,56,8,567,7,45,321,4,234,23,5,45,7,6,978,4,523,4,21,3,45,7,69,8,54,623,45,23,4,34,656,89,7689,56,23,4,234,23,5,456,5,467,67,8,4,5,34,345,45,6,324,3,46,568,789,78,56,3,45,34,5,47,6,9,5,2345,45,7,67,9,6789,6,34,52,346,56,8,789,67,423,4,23,5,345,34,6,4,12,3,123,54,56,546,7,56,8756,78,67,8,67,45,6,3,423,14,12,34,324,56,47,56,78,567,8,5,634,25,123,4,3,57,89,789,65,63,1,24,3,46,68,67,8,4,5,1235,45,7,67,8,66,312,5,458,678,9,64);


    System.out.println(list);
    Long begin=System.currentTimeMillis();
    //方法一:使用java8新特性stream进行List去重
    List newList = (List)list.stream().distinct().collect(Collectors.toList());
    System.out.println("java8stream去重:"+newList.size() +" left ,Time-cost:"+(System.currentTimeMillis()-begin)+" ms.");

    //方法2:set集合判断去重,不打乱顺序
    Set set1 = new HashSet();
    List newList1 = new ArrayList();
    for (Object integer : list) {
        if(set1.add(integer)) {
            newList1.add(integer);
        }
    }
    System.out.println("set集合判断去重:"+newList1.size() +" left ,Time-cost:"+(System.currentTimeMillis()-begin)+" ms.");

    //方法3:遍历后判断赋给另一个list集合
    List newList2 = new ArrayList();
    for (Object integer : list) {
        if(!newList2.contains(integer)){
            newList2.add(integer);
        }
    }
    System.out.println("赋值新list去重:"+newList2.size() +" left ,Time-cost:"+(System.currentTimeMillis()-begin)+" ms.");

    //方法4:set和list转换去重
    Set set2 = new HashSet();
    List newList3 = new ArrayList();
    set2.addAll(list);
    newList3.addAll(set2);
    System.out.println("set和list转换去重: "+newList3.size() +" left ,Time-cost:"+(System.currentTimeMillis()-begin)+" ms.");

运行结果与分析

运行结果

  1. 在基础类型的去重验证中,多次试验运行,使用java8特性的stream进行去重的效率始终优于其他方法。(关于stream的使用可以参照我的其他博客)
  2. 使用方法二的效率在多次验证中貌似也要优于使用方法四的基于API的转换,这里简单分析可能在于两次addAll()的转换导致了更多的开销。
  3. 方法三嘛没有什么亮点,效率也一般
  4. 不过由于没有进行大数据样本的测试,对于大数据的处理也暂时还是不要考虑用这么直接粗暴的内存计算了。

对于复杂类型~对象的去重

复杂类型比较的基础介绍

这里主要介绍复杂类型去重与简单类型去重的区别,首先需要知道的是简单类型的存储与使用其实是共享的,即下面的代码:
int i=127;
int j=127;
其实i与j指向的是同一个内存地址存储的值。(这里就不深入讲Interger类型在封包解包的操作中与i的差别或者String类型使用的差别了)
当对i,j进行==比较时,其实比较的是两个变量的内存地址,由于基础类型的存储特性,显然同样的值也就意味着同样的内存地址;
而当使用Equals()方法对对象进行比较时,就不一样了,默认的Object.Equals()方法使用的仍然是 == 比较即内存地址比较,
但在复杂对象的比较重,比较内存地址一般是无意义的,我们关注的是对象的类型或对象容纳值的等同意义。
因此在复杂类型的比较中我们往往会重写Equals()方法,为了提高比较的效率,又需要使用或重写HashCode()方法;
为了提高"比较"这件事的可复用性,即如果我们需要或可能需要支持多种比较规则,并支持排序等操作的话,我们就可以通过实现Comparable接口来进行对象的比较。

重写Equals()方法

@Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
People p = (People) arg0;
return name.equals(p.name) && phoneNumber.equals(p.phoneNumber);
}

@Override
public int hashCode() {
    // TODO Auto-generated method stub
    String str = name + phoneNumber;
    return str.hashCode();
}

原有Equals

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = count;
            if (n == anotherString.count) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = offset;
                int j = anotherString.offset;
                while (n-- != 0) {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }
        }
        return false;
    }

比较两个对象时,首先先去判断两个对象是否具有相同的地址,如果是同一个对象的引用,则直接放回true;如果地址不一样,则证明不是引用同一个对象,接下来就是挨个去比较两个字符串对象的内容是否一致,完全相等返回true,否则false。

hashCode()
    public int hashCode() {
        int h = hash;
        if (h == 0 && count > 0) {
            int off = offset;
            char val[] = value;
            int len = count;
            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }

集合排序

Comparable接口实现

1. 对象实现Comparable接口,
2. 重写compareTo方法,
	public class Man implements Comparable<Man>{ 
	//TODO 中间代码省略 
	    @Override 
	    public int compareTo(Man m) { 
	        return this.age-m.age; // 0等于,>0大于, <0小于
	    } 
	} 

3.排序
Collections.sort(manList);

Comparator比较器实现

对任意类型集合对象进行整体排序,排序时将此接口的实现传递给Collections.sort方法或者Arrays.sort方法排序.
实现int compare(T o1, T o2);方法,返回正数,零,负数各代表大于,等于,小于。

单一条件排序:(多条件无非是在s1,s2第一个条件相等时再嵌套一层对于第二个条件的判断,其实都一样)
举例:

	List<Student> stus = new ArrayList<Student>(){
		{
			add(new Student("张三", 30));	
			add(new Student("李四", 20));	
			add(new Student("王五", 60));	
		}
	};
	//对users按年龄进行排序
	Collections.sort(stus, new Comparator<Student>() {

		@Override
		public int compare(Student s1, Student s2) {
			// 升序
			//return s1.getAge()-s2.getAge();
			return s1.getAge().compareTo(s2.getAge());
			// 降序
			// return s2.getAge()-s1.getAge();
			// return s2.getAge().compareTo(s1.getAge());
		}
	});
	// 输出结果
	...

注: 还可以使用lambda表达式简化代码, 前提是JDK8开发环境, 如下:

	List<Student> stus = new ArrayList<Student>(){
		{
			add(new Student("张三", 30));	
			add(new Student("李四", 20));	
			add(new Student("王五", 60));	
		}
	};
	//对users按年龄进行排序
	Collections.sort(stus, (s1,s2)->(s1.getAge()-s2.getAge()));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 学生类可以实现Comparable接口来进行比较实现Comparable接口需要覆盖其中的compareTo方法,该方法需要返回一个整数值,表示当前对象与另一个对象的大小关系。在学生类中,可以根据学生的某个属性(如学号、姓名、年龄等)来进行比较,比如: ``` public class Student implements Comparable<Student> { private String name; private int age; private String studentId; // 构造方法、getter和setter省略 @Override public int compareTo(Student o) { // 按照学号进行比较 return this.studentId.compareTo(o.getStudentId()); } } ``` 在上面的代码中,我们实现Comparable接口,并覆盖了compareTo方法,按照学号进行比较。在比较时,我们调用了String类的compareTo方法,该方法比较两个字符串的字典序大小,并返回一个整数值,表示它们的大小关系。如果当前对象的学号小于另一个对象的学号,则返回一个负整数;如果当前对象的学号等于另一个对象的学号,则返回;如果当前对象的学号大于另一个对象的学号,则返回一个正整数。这样,我们就可以通过调用Collections.sort方法来对学生对象进行排序了。 ### 回答2: 在Java中,可以通过定义学生类并覆盖Object类中的方法实现Comparable接口,从而对学生对象进行比较排序。 在定义学生类时,需要重写Object类中的equals方法实现对学生对象的相等性的判断。另外,还需要实现Comparable接口中的compareTo方法来定义两个学生对象之间的大小关系。 例如,以下是一个基本的学生类的实现: ``` public class Student implements Comparable<Student> { private String name; private int age; private int grade; public Student(String name, int age, int grade) { this.name = name; this.age = age; this.grade = grade; } @Override public boolean equals(Object obj) { if (obj instanceof Student) { Student other = (Student) obj; return this.name.equals(other.name) && this.age == other.age && this.grade == other.grade; } return false; } @Override public int compareTo(Student other) { if (this.grade != other.grade) { return Integer.compare(other.grade, this.grade); } else if (this.age != other.age) { return Integer.compare(this.age, other.age); } else { return this.name.compareTo(other.name); } } } ``` 在这个示例中,equals方法通过比较学生的姓名、年龄和成绩来判断两个学生对象是否相等。compareTo方法则按照成绩、年龄和姓名的顺序,依次比较两个学生对象之间的大小关系。 除了重写equals和compareTo方法外,还可以在学生类中定义其他方法和属性,以满足实际需求。例如,可以添加一个toString方法,用于将学生对象转换为字符串表示: ``` @Override public String toString() { return String.format("%s (%d years old, grade: %d)", name, age, grade); } ``` 这样,在排序或输出学生对象时,可以通过调用toString方法来获取学生对象的字符串表示,方便显示和调试。 ### 回答3: 学生类是一种在面向对象编程中常见的类,它用于描述学生这一实体,并包含一系列属性和方法,比如姓名、年龄、性别、成绩等。在Java中,所有类都是直接或间接继承自Object类,而Object类中包含了一些通用的方法,比如equals()、toString()等,这些方法可以被所有的类所使用。另外,如果我们希望对学生对象进行排序,可以通过实现Comparable接口实现。 在定义学生类时,通常会重写Object类中的一些方法,比如toString()方法,以便在需要打印学生对象时能够清晰地显示出其属性信息。另外,当我们想对学生对象进行排序时,需要实现Comparable接口,并在其中重写compareTo()方法。该方法用于比较两个学生对象的大小关系,返回一个int类型的结果,其中正数表示当前对象大于参数对象,负数表示当前对象小于参数对象,零表示两个对象相等。 具体实现时,我们需要在学生类中实现Comparable接口,并重写compareTo()方法。在该方法中,我们可以根据学生的某个属性(如成绩、年龄等)来进行比较,并返回相应的结果。例如: public class Student implements Comparable<Student> { private String name; private int age; private int score; //省略构造方法和其他方法 @Override public int compareTo(Student o) { if (this.score > o.score) { return 1; } else if (this.score < o.score) { return -1; } else { return 0; } } } 在上述代码中,我们实现Comparable<Student>接口,并重写了compareTo()方法。该方法比较当前学生对象和传入的参数学生对象的成绩大小,并返回相应的比较结果。如果当前学生对象的成绩大于参数对象的成绩,返回1;如果小于参数对象的成绩,则返回-1;如果两个对象的成绩相等,则返回0。 通过实现Comparable接口,我们可以方便地对学生对象进行排序,例如: Student s1 = new Student("张三", 18, 90); Student s2 = new Student("李四", 20, 80); Student s3 = new Student("王五", 19, 85); List<Student> list = new ArrayList<>(); list.add(s1); list.add(s2); list.add(s3); Collections.sort(list); for (Student s : list) { System.out.println(s.getName() + " " + s.getScore()); } 输出结果为: 李四 80 王五 85 张三 90 可以看出,通过实现Comparable接口,我们可以很方便地对学生对象根据成绩进行排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值