Java Collection框架(五)集合与泛型 比较器 hashCode与equals

​集合与泛型,如何使用比较器,hashCode与equals的区别 

2014我在郎木寺敖包下,友人所拍

微信公众号

本篇看一下关于集合的一些知识点。

 

集合与泛型

 

首先来看一下集合与泛型的结合使用,JDK1.5之后为了约束类型,更新了泛型技术。

 

ArrayList不加泛型

 

 

 

我们首先利用ArrayList来盛放元素,由于没有泛型限制,我们添加了三种类型,可以通过编译,获取时可以根据不同类型强制转换,或者使用Object盛放获取的元素,都可以通过编译,并可以执行成功。

 

ArrayList<Object>

 

 

接下来为ArrayList添加<Object>泛型限制,使用相同手段获取元素,也可以编译通过,并执行成功,与不加泛型限制的ArrayList效果相同。

 

ArrayList<Integer>

 

 

接下来为ArrayList arrayList3添加<Integer>泛型,并将arrayList1赋值给它,这时arrayList3的get操作只能返回Integer,但是实际上get(2)为一个String对象,这时就会抛出java.lang.ClassCastException类型转换异常。

 

 

此时ArrayList里的元素只能以Integer的形式获取,添加,这样就限制了ArrayList的元素类型,可以看到add方法添加别的类型元素无法通过编译。

 

ArrayList<?>

 

 

接下来我们看一下ArrayList<?>的用法,当我们创建一个ArrayList<?> arrayList4时,发现无法add任何元素,但是可以将arrayList1的所有元素赋值给arrayList4。<?>通常用来作为参数接收外部的集合,或者返回不确定类型的参数。

 

List<T>只能接收一种类型的参数,实际上JDK还为我们提供了更多选择,接下来我们来看一下<? extends T>与<? super T>这两种泛型语法在集合中的使用方式。

 

<? extends T>

 

可以赋值给T类型及T的子类类型的集合,上界为T,取出来的类型带有泛型限制,向上强制转换为T。null可以表示任何类型,所以除了null,任何元素都无法放入<? extends T>类型集合。

 

<? super T>

 

可以赋值给T类型及T的父类类型的集合,下界为T,<? super T>该类型集合内的元素泛型丢失,无法从中获取元素。

 

对于<? extends T>与<? super T>来讲,一个主要适用于消费或者获取元素,一个则是生产和放入元素。

 

接下来举一个例子来看一下这两种泛型语法在集合中的应用。

 

首先创建三个类,Animal,Dog,Huskie.Huskie继承Dog,Dog继承Animal。

 

 

 

 

然后我们分别创建三个不同泛型的集合,并向内add对应泛型类型的元素。

 

 

此时我们可以看到,三种类型的集合都可以添加元素。接下来以Dog为基准,创建<? extends Dog>与<? super Dog>两种类型的ArrayList。

 

 

此时,我们将animal集合赋值给ArrayList<? extends Dog> dogExtends时会发生编译错误,动物也包含猫,鸟类等,是不可以赋值给狗类型下的。

 

但是我们将animal集合赋值给dogSuper集合是可以的,huskie赋值给dogSuper时会编译错误,因为dogSuper只能接受Dog类型及其父类的元素集合赋值。

 

两个集合都可以被赋值Dog类型的元素集合。

 

 

 

接下来我们对这两种集合进行赋值。

 

dogExtends,任何元素都无法赋值,是因为多有List<? extends T>泛型限制除null以外,所有类型元素都无法进行add操作。

 

dogSuper可以添加元素,但是只能添加Dog本身及其子类,所以animal类型元素无法add。

 

 

接下来是get操作,dogExtends在get操作时,可以返回Object类,可以返回Dog类,但是无法返回Huskie类型,因为集合中可能存在萨摩或者柯基。

 

dogSuper进行get操作因为类型丢失,只能返回Object类型。

 

如果一个集合一直存放元素属于add first,我们可以使用<? super T>泛型限制,如果一个集合一直获取元素则属于get first,我们可以使用<? extends T>泛型限制。

 

比较器 Comparable与Comparator

 

我们在平时对元素进行排序中,通常使用比较器来实现对象的排序,常用的接口两个Comparable与Comparator,第一个为内部比较器,第二个为外部比较器。

 

先来看一下Comparable如何使用,为什么叫它内部比较器,首先我们创建一个Student类,有名字与年龄两个成员变量,我们将age作为首要条件排序,name作为次要条件。

 

 

接下来我们让Student继承Comparable接口,并实现它的compareTo方法。

 

 

 

因为我们需要在Student类内部实现,所以我们就把Comparable比作一个内部比较器。接下来我们看一下效果,创建一个TreeSet无序集合,创建元素放入,使用迭代器全部迭代并打印。

 

 

在放入元素时是无序的,然后看一下迭代结果:

 

 

首先按照年龄排序,年龄相同,我们使用String类的内部比较器进行比较可以看到xiao6与xiao7年龄相同,但是会按照name排序。

 

接下来看一下外部比较器Comparator的使用方式,使用Comparator无需修改Student类,只需要配合Collection的sort方法使用即可实现集合内元素排序。

 

创建一个普通的Bean,Human类。

 

 

创建一个外部比较器MyComparator实现Comparator接口实现compare方法。

 

 

接下来我们创建一个LIst集合并随机放入human元素,并使用Collections.sort方法用MyComparator对List进行排序后遍历List并打印元素。

 

 

打印结果为:

 

 

hashCode与equals

 

hashCode与equals都是Object类的方法。

 

在哈希相关的容器中,需要大量的比对,根据对象生成的哈希值可以使存取速度更快,hashCode与equals两个方法都是用来比较两个对象是否相等的方法。

 

由于hashCode方法计算哈希值可能存在哈希冲突的情况,所以还需要equals方法进行比较一次equals方法是绝对可靠的,hashCode不一定可靠。

 

所以如果两个对象的equals相等则他们的hashCode一定相等,任何时候覆写equals都必须同时覆写hashCode。

 

当hashCode相同时,需要再调用一下equals方法比较,如果hashCode都不同则直接跳过equals直接返回不同,是一个短路操作。

 

在hashset里要求对象不能重复,内部要对添加进去的每个对象进行对比,而它的对比规则就是像上面说的那样,先比较hashCode()产生的哈希值,如果哈希值相同,再用equals方法验证,如果哈希值都不同,则直接跳过equals方法,这样对比的效率就很高了。

 

所以好的哈希算法应该尽可能的让元素分配均匀,降低冲突的可能。

 

接下来我们举一个实际使用的例子,set集合中元素是不可重复的,但是由于哈希值基本上是由对象地址进行相关计算得到的int类型数据,所以例如我们在创建上Student类时,哪怕内容一样,在set中也会存储三个Student对象。

 

 

执行结果:

 

 

由于没有覆写hashCode方法,Object.hashCode()方法默认根据每一个对象的地址生成不同的哈希码,所以hashCode不相等则直接判断两个对象不相等,所以我们需要覆写hashCode方法与equals方法来根据id和name来保证元素的不同。

 

 

在这里覆写了Student类的hashCode方法与equals方法,来保证元素id的唯一性。

 

 

再次运行向hashSet中添加元素,返回size为1.

 

 

-END-

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值