集合框架-Set集合
代码:
Collection c = new ArrayList();
c.add("hello");
c.add("world");
c.add("java");
System.out.println(c);
为什么c输出的不是地址值呢?
A:Collection c = new ArrayList();
这是多态,所以输出c的toString()方法,其实是输出ArrayList的toString()
B:看ArrayList的toString()
而我们在ArrayList里面却没有发现toString()。
以后遇到这种情况,也不要担心,你认为有,它却没有,就应该去它父亲里面看看。
C:toString()的方法源码
public String toString() {
Iterator<E> it = iterator(); //集合本身调用迭代器方法,得到集合迭代器
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next(); //e=hello,world,java
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
//[hello, world, java]
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
需求:用户登录注册案例。
假如用户类的内容比较对,将来维护起来就比较麻烦,为了更清晰的分类,我们就把用户又划分成了两类
用户基本描述类
成员变量:用户名,密码
构造方法:无参构造
成员方法:getXxx()/setXxx()
用户操作类
登录,注册
分包:
A:功能划分
B:模块划分
C:先按模块划分,再按功能划分
今天我们选择按照功能划分:
用户基本描述类包 cn.itcast.pojo
用户操作接口 cn.itcast.dao
用户操作类包 cn.itcast.dao.impl
今天是集合实现,过几天是IO实现,再过几天是GUI实现,就业班我们就是数据库实现
用户测试类 cn.itcast.test
// 为了让多个方法能够使用同一个集合,就把集合定义为成员变量
// 为了不让外人看到,用private
// 为了让多个对象共享同一个成员变量,用static
private static ArrayList<User> array = new ArrayList<User>();
* Collection
* |--List
* 有序(存储顺序和取出顺序一致),可重复
* |--Set
* 无序(存储顺序和取出顺序不一致),唯一
*
* HashSet:它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。
* 注意:虽然Set集合的元素无序,但是,作为集合来说,它肯定有它自己的存储顺序,
* 而你的顺序恰好和它的存储顺序一致,这代表不了有序,你可以多存储一些数据,就能看到效果。
// 创建集合对象
Set<String> set = new HashSet<String>(); // 创建并添加元素
set.add("hello");
set.add("java");
set.add("world");
set.add("java");
set.add("world"); // 增强for
for (String s : set) {
System.out.println(s);
}
* HashSet:存储字符串并遍历
* 问题:为什么存储字符串的时候,字符串内容相同的只存储了一个呢?
* 通过查看add方法的源码,我们知道这个方法底层依赖 两个方法:hashCode()和equals()。
* 步骤:
* 首先比较哈希值
* 如果相同,继续走,比较地址值或者走equals()
* 如果不同,就直接添加到集合中
* 按照方法的步骤来说:
* 先看hashCode()值是否相同
* 相同:继续走equals()方法
* 返回true: 说明元素重复,就不添加
* 返回false:说明元素不重复,就添加到集合
* 不同:就直接把元素添加到集合
* 如果类没有重写这两个方法,默认使用的Object()。一般来说不同相同。
* 而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
* 需求:存储自定义对象,并保证元素的唯一性
* 要求:如果两个对象的成员变量值都相同,则为同一个元素。
*
* 目前是不符合我的要求的:因为我们知道HashSet底层依赖的是hashCode()和equals()方法。
* 而这两个方法我们在学生类中没有重写,所以,默认使用的是Object类。
* 这个时候,他们的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。
HashSet集合的add()方法的源码:
interface Collection {
...
}
interface Set extends Collection {
...
}
class HashSet implements Set {
private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) { //e=hello,world
return map.put(e, PRESENT)==null;
}
}
class HashMap implements Map {
public V put(K key, V value) { //key=e=hello,world
//看哈希表是否为空,如果空,就开辟空间
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
//判断对象是否为null
if (key == null)
return putForNullKey(value);
int hash = hash(key); //和对象的hashCode()方法相关
//在哈希表中查找hash值
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
//这次的e其实是第一次的world
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
//走这里其实是没有添加元素
}
}
modCount++;
addEntry(hash, key, value, i); //把元素添加
return null;
}
transient int hashSeed = 0;
final int hash(Object k) { //k=key=e=hello,
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode(); //这里调用的是对象的hashCode()方法
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
}
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
* LinkedHashSet:底层数据结构由哈希表和链表组成。
* 哈希表保证元素的唯一性。
* 链表保证元素有素。(存储和取出是一致)
// 创建集合对象
LinkedHashSet<String> hs = new LinkedHashSet<String>(); // 创建并添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
hs.add("java"); // 遍历
for (String s : hs) {
System.out.println(s);
}
* TreeSet:能够对元素按照某种规则进行排序。
* 排序有两种方式
* A:自然排序
* B:比较器排序
*
* TreeSet集合的特点:排序和唯一
*
* 通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法。
TreeSet的add()方法的源码解析:
interface Collection {...}
interface Set extends Collection {...}
interface NavigableMap {
}
class TreeMap implements NavigableMap {
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
}
class TreeSet implements Set {
private transient NavigableMap<E,Object> m;
public TreeSet() {
this(new TreeMap<E,Object>());
}
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
}
真正的比较是依赖于元素的compareTo()方法,而这个方法是定义在 Comparable里面的。
所以,你要想重写该方法,就必须是先 Comparable接口。这个接口表示的就是自然排序。
* 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
* TreeSet集合保证元素排序和唯一性的原理
* 唯一性:是根据比较的返回是否是0来决定。
* 排序:
* A:自然排序(元素具备比较性)
* 让元素所属的类实现自然排序接口 Comparable
* B:比较器排序(集合具备比较性)
* 让集合的构造方法接收一个比较器接口的子类对象 Comparator
// 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
// 而匿名内部类就可以实现这个东西
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
HashSet存储元素保证唯一性的代码及图解:

TreeSet存储元素自然排序和唯一的图解:

集合框架-Set集合的更多相关文章
- JAVASE02-Unit04: 集合框架 、 集合操作 —— 线性表
Unit04: 集合框架 . 集合操作 -- 线性表 操作集合元素相关方法 package day04; import java.util.ArrayList; import java.util.Co ...
- 浅谈集合框架六——集合扩展:Arrays工具类、集合与数组相互转换方式;
最近刚学完集合框架,想把自己的一些学习笔记与想法整理一下,所以本篇博客或许会有一些内容写的不严谨或者不正确,还请大神指出.初学者对于本篇博客只建议作为参考,欢迎留言共同学习. 之前有介绍集合框架的体系 ...
- 浅谈集合框架五——集合框架扩展:Collections工具类的使用,自定义比较器
最近刚学完集合框架,想把自己的一些学习笔记与想法整理一下,所以本篇博客或许会有一些内容写的不严谨或者不正确,还请大神指出.初学者对于本篇博客只建议作为参考,欢迎留言共同学习. 之前有介绍集合框架的体系 ...
- 浅谈集合框架四——集合扩展:集合循环输出方式及list输出方式的效率对比
最近刚学完集合框架,想把自己的一些学习笔记与想法整理一下,所以本篇博客或许会有一些内容写的不严谨或者不正确,还请大神指出.初学者对于本篇博客只建议作为参考,欢迎留言共同学习. 之前有介绍集合框架的体系 ...
- 集合框架-TreeSet集合
1 package cn.itcast.p5.treeset.demo; 2 3 import java.util.Iterator; 4 import java.util.TreeSet; 5 6 ...
- 集合框架-HashSet集合(无序唯一)
1 package cn.itcast.p4.hashset.demo; 2 3 import java.util.HashSet; 4 import java.util.Iterator; 5 /* ...
- Java基础之集合框架——使用集合Vector<>挑选演员(TryVector)
控制台程序. public class Person implements Comparable<Person> { // Constructor public Person(String ...
- java集合框架-List集合ArrayList和LinkedList详解
List 集合源码剖析 ✅ ArrayList 底层是基于数组,(数组在内存中分配连续的内存空间)是对数组的升级,长度是动态的. 数组默认长度是10,当添加数据超越当前数组长度时,就会进行扩容,扩容长 ...
- java 集合框架 Java集合&List的实现
原文: https://blog.csdn.net/u012658346/article/details/50917068 一.java集合类图如下所示: 上述类图中,实线边框的是实现类,比如Arra ...
- 集合框架-Map集合
* Map集合和Collection集合的区别? * Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的.可以把这个理解为:夫妻对 * Collection集合存储元素是单独出现的, ...
随机推荐
- UGUI之Scrollbar使用
这个效果主要用到了3个组件(对象): 1:Scrollbar对象 滚动条 2:Scroll Rect组件 让对象具有滑动效果 3:Mask组件 遮罩层.把多余的部分隐藏不显示 Scrollbar ...
- 8.一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
注:这题胜在区分度高,知识点覆盖广,再不懂的人,也能答出几句, 而高手可以根据自己擅长的领域自由发挥,从URL规范.HTTP协议.DNS.CDN.数据库查询. 到浏览器流式解析.CSS规则构建.lay ...
- C#会对于未赋值的变量/成员变量,给予一个初始值吗?
如果我有程序如下: C# code ? 1 2 3 4 5 6 7 public class My { public bool b; public ...
- Mongodb 分组查询例子
db.tblCard.aggregate([ { $match: { "sNo": { " ...
- SpringBoot的json序列化及时间序列化处理
使用场景:前台的多种时间格式存入后台,后台返回同时的时间格式到前台. 1 在config目录下配置jscksonConfig.java package com.test.domi.config; im ...
- Java类访问控制
public protected default private 本类 可见 可见 可见 可见 本类所在包 可见 可见 可见 不可见 其他包中的子类 可见 可见 不可见 不可见 其他包中的非子类 ...
- 用Hexo在GitHub上搭建个人博客
我用Hexo在GitHub上搭建好了自己的博客,我的这第一篇博客就来说说搭建的过程. 1 环境配置 本文使用环境如下: Windows 10 node.js v8.1.3 git v2.13.2 np ...
- Rpgmakermv(14)Archeia_Steamworks
作用: 接入steam成就 -----------------------------------------------------------------------------------原文: ...
- libvirt_python
一.Connections 连接函数接口libvirt.open(name); //可读写方式连接上QEMU 参数说明: name:连接名称libvirt.openAuth(uri, auth, fl ...
- 定时调度任务quartz
依赖 <!-- quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> < ...