jdk集合常用方法分析之HashSet和TreeSet
HashSet常用方法介绍
public boolean add(E e)
public boolean isEmpty()
void clear()
public Iterator<E> iterator()
int size()
详细方法详见JDK帮助文档
Object的equals方法和hashCode方法
1、 Object类equals方法的特点:
a) 自反性:x.equals(x)应该返回true
b) 对称性:x.equels(y)为true,那么y.equals(x)也为true
c) 传递性:x.equels(y)为true并且y.equels(z)为true,那么x.equels(z)也应该为true
d) 一致性:x.equels(y)的第一次调用为true,那么x.equels(y)的第二次、第三次、第n次调用也应该为true,前提条件是在比较之间没有修改x也没有修改y
e) 对于非空引用x,x.equels(null)返回false
2、 Object类hashCode方法的特点:
a) 在java应用的一次执行过程中,对于同一个对象的hashCode方法的多次调用,他们应该返回同样的值(前提是该对象的信息没有发生变化);
b) 对于两个对象来说,如果使用equals方法比较返回true,那么这两个对象的hashCode值一定是相同的;
c) 对于两个对象来说,如果使用equals方法比较返回false,那么这两个对象的hashCode值不要求一定不同(可以相同,也可以不同),但是如果不同则可以提高应用的性能;
d) 对于Object类来说,不同Object对象的hashCode的值是不同的(Object类的hashCode值表示的是对象的地址);Object的toString()方法默认调用的是:
getClass().getName() + '@' + Integer.toHexString(hashCode())
3、 如果我们重写equals方法,那么也要重写hashCode方法,反之亦然;
HashSet常用方法源码分析
构造方法
public HashSet() {
map = new HashMap<E,Object>();
} private transient HashMap<E,Object> map;
add()
public boolean add(E e) {
return map.put(e, PRESENT)==null;
} private static final Object PRESENT = new Object(); //同一个set中的PRESENT对象一致
remove()
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
size()
public int size() {
return map.size();
}
iterator ()
public Iterator<E> iterator() {
return map.keySet().iterator();
}
常用方法源码分析总结:
1、 set可以添加null;
2、 在使用HashSet时,hashCode()方法会得到调用,判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致,如果不一致,直接添加进去;如果一致,再进行equals方法的比较,equals方法如果返回true,表示对象已经添加进去了,就不会再添加新的对象,否则就加进去;
3、 HashSet底层是使用HashMap实现的,当调用add方法将对象添加到Set中时,实际上是将该对象作为底层所维护的Map对象的key,而value则都是同一个Object对象(该对象我们用不上)。在HashMap中,同一个key,后添加的value会覆盖前一个value;
4、 当重写equals方法的时候必须要重写hashCode方法,如果一个类的两个对象的equals方法比较结果为true,那么该两个对象必须具有相同的hashCode值。
HashSet示例分析
示例1:添加两个相同的对象
public class HashSetDemo03 {
public static void main(String[] args) {
HashSet set = new HashSet();
People p1 = new People("zhangsan");// p1存放的是zhangsan的引用地址 set.add(p1);
set.add(p1);
System.err.println(set);
}
} public class People {
String name;
public People(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
分析:当往set中添加Person对象的时候,先添加进去p1,在第二次还是添加p1的时候,会先调用hashCode方法(同一对象的hashCode值当然相同),再去判断equals()方法,同一对象的equals()方法肯定是true的,所以加不进去。
示例2:添加两个相同的字符串
public class HashSetDemo04 {
public static void main(String[] args) {
HashSet set = new HashSet();
String s1 = new String("a");
String s2 = new String("a");
set.add(s1);
set.add(s2);
System.out.println(set);
}
}
分析:当往set中添加是s1的时候,先添加进去s1,在第二次还是添加s1的时候,会先调用hashCode方法,String对象已经重写了hashCode()方法(根据给定的字符串用一个公式运算),hashCode值是相同的,再去判断equals()方法,String的equals()方法比较的是字符串的内容,所以equals()为true,所以加不进去。
示例3:重写equals和hashcode后对比
public class HashSetDemo05 {
public static void main(String[] args) {
HashSet set = new HashSet();
Student s1 = new Student("zhangsan");
Student s2 = new Student("zhangsan");
set.add(s1);
set.add(s2);
System.out.println(set);
}
}
public class Student {
String name;
public Student(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
分析:
未重写equals和hashcode方法之前:s1和s2是不同的两个对象,由于hashCode存放的是引用的地址,所以他们是不同的,就直接加进去了。
重写equals()和hashCode()方法之后,hashCode就按照name进行取值,所以他们的hashCode相等,然后比较equals()方法,而我们自己也按照name重写了equals()方法。所以equals为true,就不会加进去。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
示例3:HashSet常用迭代方法
public class HashSetDemo06 {
public static void main(String[] args) {
HashSet set = new HashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e"); Iterator iter = set.iterator();
while (iter.hasNext()) {
String value = (String) iter.next();
System.out.println(value);
} System.out.println("--------------"); for (Iterator iterator = set.iterator(); iterator.hasNext();) {
String s = (String) iterator.next();
System.out.println(s);
}
}
}
TreeSet
HashSet是在输出的时候是没有顺序的,而TreeSet是能够按照指定的顺序进行输出的。
TreeSet的构造方法
public TreeSet(Comparator<? super E> comparator)
在构造方法中需要我们事先自定义好一个实现Comparator接口的方法,并重写它的int compare(T o1,T o2)方法。
如何定义自己的Comparator
public class TreeSetDemo02 {
public static void main(String[] args) {
TreeSet<Order> set = new TreeSet<Order>(new OrderComparator());
set.add(new Order(10));
set.add(new Order(20));
set.add(new Order(30));
set.add(new Order(40)); for (Order order : set) {
System.out.println(order);
}
}
} class OrderComparator implements Comparator<Order> {
@Override
public int compare(Order o1, Order o2) {
return -(o1.count - o2.count);
}
} class Order {
int count;
public Order(int count) {
this.count = count;
}
public String toString() {
return String.valueOf(count);
}
}
我们可以在OrderComparator类中的compare方法中按照自己的意愿进行重写以实现升序或者降序排列。
public class TreeSetDemo03 {
public static void main(String[] args) {
TreeSet set = new TreeSet(new MyComparator());
set.add("A");
set.add("F");
set.add("E");
set.add("D");
set.add("a");
set.add("C");
set.add("B");
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
String s = (String) iterator.next();
System.out.println(s);
}
}
} class MyComparator implements Comparator {
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
}
我们可以在MyComparator类中的compare方法中按照字母的降序排列。
jdk集合常用方法分析之HashSet和TreeSet的更多相关文章
- jdk集合常用方法分析之ArrayList&LinkedList&以及两者的对比分析
集合使用注意事项: 1.集合当中只能放置对象的引用,无法放置原生数据类型,我们需要使用原生数据类型的包装类才能加入到集合当中去(JDK5之后会进行自动的装箱和拆箱操作,表面上看集合中是可以直接放置原生 ...
- java集合系列——Set之HashSet和TreeSet介绍(十)
一.Set的简介 Set是一个不包含重复元素的 collection.更确切地讲,set 不包含满足 e1.equals(e2) 的元素.对 e1 和 e2,并且最多包含一个为 null 的元素. S ...
- JDK源码分析(五)——HashSet
目录 HashSet概述 内部字段及构造方法 存储元素 删除元素 包含元素 总结 HashSet概述 从前面开始,已经分析过集合中的List和Map,今天来介绍另一种集合元素:Set.这是JDK对 ...
- (9)集合之Set,HashSet,TreeSet
TreeSet子类 注意事项: 1.向TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储 2.往TreeSet添加元素的时候,如果元素本身不具备自 ...
- JDK源码学习笔记——HashSet LinkedHashSet TreeSet
你一定听说过HashSet就是通过HashMap实现的 相信我,翻一翻HashSet的源码,秒懂!! 其实很多东西,只是没有静下心来看,只要去看,说不定一下子就明白了…… HashSet 两个属性: ...
- Java集合源码分析(六)TreeSet<E>
TreeSet简介 TreeSet 是一个有序的集合,它的作用是提供有序的Set集合.它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable, j ...
- Java集合详解7:HashSet,TreeSet与LinkedHashSet
今天我们来探索一下HashSet,TreeSet与LinkedHashSet的基本原理与源码实现,由于这三个set都是基于之前文章的三个map进行实现的,所以推荐大家先看一下前面有关map的文章,结合 ...
- HashSet LinkedHashSet TreeSet 分析
1.HashSet分析 hashset 底层是hash表,就是hashMap,是无序的,唯一的.也就是说,它的底层其实就是一个HashMap key 值的组成值.所以具有唯一性. public Ha ...
- Java集合系列(三):HashSet、LinkedHashSet、TreeSet的使用方法及区别
本篇博客主要讲解Set接口的三个实现类HashSet.LinkedHashSet.TreeSet的使用方法以及三者之间的区别. 注意:本文中代码使用的JDK版本为1.8.0_191 1. HashSe ...
随机推荐
- Android手机平板两不误,使用Fragment实现兼容手机和平板的程序
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8744943 记得我之前参与开发过一个华为的项目,要求程序可以支持好几种终端设备,其 ...
- Maven 依赖管理
1 概念介绍 之前我们说过,maven 坐标能够确定一个项目.换句话说,我们可以用它来解决依赖关系.在 POM 中,依赖关系是在 dependencies部分中定义的.在上面的 POM 例子中,我们 ...
- 【hibernate】之标注枚举类型@Enumerated(转载)
实体Entity中通过@Enumerated标注枚举类型,例如将CustomerEO实体中增加一个CustomerType类型的枚举型属性,标注实体后的代码如下所示. @Entity @Table(n ...
- (WPF) 文件和文件夹选择对话框。
点击button,选择一个excel文件,并将文件名显示在textbox上. private void btnSelectErrorTableFile_Click(object sender, Rou ...
- sql语句(mysql)
MySQL1.数据库数据类型 int 整型 double 浮点型 如double(5,2)最多5位,其中有两位小数 char:固定长度字符串,如char(255),数据长度不足会用空格补足 varch ...
- CF 518 D. Ilya and Escalator
Ilya got tired of sports programming, left university and got a job in the subway. He was given the ...
- python随文档
UNIX网络编程--socket的keep http://www.68idc.cn/help/opersys/unixbsd/20150731471448.html 云计算学习和实践: 原创<每 ...
- Hololens开发笔记之Gesture手势识别(基本介绍)
手势识别是HoloLens交互的重要输入方法之一.HoloLens提供了底层API和高层API,可以满足不同的手势定制需求.底层API能够获取手的位置和速度信息,高层API则借助手势识别器来识别预设的 ...
- Java设计模式—生产者消费者模式(阻塞队列实现)
生产者消费者模式是并发.多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据.这篇文章我们来看看什么是生产者消费者模式,这个问 ...
- freemodbus线圈中的位操作
连续位的操作函数: xMBUtilSetBits(UCHAR * ucByteBuf, USHORT usBitOffset, UCHAR ucNBits,UCHAR ucValue): xMBUti ...