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

List去重

    • 简单类型的去重
      • 代码示例
      • 运行结果与分析
    • 对于复杂类型~对象的去重
      • 复杂类型比较的基础介绍
      • 重写Equals()方法
      • 原有Equals
    • 集合排序
      • Comparable接口实现
      • Comparator比较器实现

简单类型的去重

代码示例

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

		@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 stus = new ArrayList(){
		{
			add(new Student("张三", 30));	
			add(new Student("李四", 20));	
			add(new Student("王五", 60));	
		}
	};
	//对users按年龄进行排序
	Collections.sort(stus, (s1,s2)->(s1.getAge()-s2.getAge()));

你可能感兴趣的:(Java集合,数据结构)