Set接口

Set接口用来表示:一个不包含“重复元素”的集合
Set接口中并没有定义特殊的方法,其方法多数都和Collection接口相同。

重复元素的理解
通常理解:拥有相同成员变量的对象称为相同的对象,如果它们出现在同一个集合中的话,称这个集合拥有重复的元素

HashSet中对重复元素的理解:和通常意义上的理解不太一样!
两个元素(对象)的hashCode返回值相同,并且equals返回值为true时(或者地址相同时),才称这两个元素是相同的。

TreeSet中对重复元素的理解:元素的compareTo方法或者集合的比较器compare方法返回值为0则认为这两个元素是相同的元素。

1 Set接口的方法

可知Set接口并没有比父类Collection接口提供更多的新方法。

2、HashSet类

线程不安全,存取速度快
它的大多数方法都和Collection相同
它不保证元素的迭代顺序;也不保证该顺序恒久不变
当HashSet中的元素超过一定数量时,会发生元素的顺序重新分配。

2.1HashSet构造方法

    public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

看来set底层是HashMap

2.2成员变量

2.3成员方法

2.4HashSet如何保证元素唯一?

考查add(Object obj)方法的实现过程:

  1. 先调用obj的hashCode方法,计算哈希值(槽位值slot:bucket)
  2. 根据哈希值确定存放的位置
  3. 若位置上没有元素,则这个元素就是第一个元素,直接添加
  4. 若此位置上已经有元素,说明还有元素的hashCode方法返回值与它相同,则调用它的equals方法与已经存在的元素进行比较
  5. 若返回值为true,表明两个元素是“相同”的元素,不能添加
  6. 若返回值为false,表明两个元素是“不同”的元素,新元素将以链表的形式添加到集合中

    

import java.util.HashSet;

/*
*自定义对象存储到HashSet中
*
*int hashCode:元素被添加时被调用,用于确认元素的槽位值
*boolean equals:当发生碰撞时,调用被添加元素的equals方法和已经存在的元素进行比较,
* true:不能添加
* false:可以添加,多个元素占用一个槽位值.以链表形式存在.
*
*/ class Worker{
// static int i = 0;
private String name;
private int age;
private String id; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Worker(String name, int age, String id) {
super();
this.name = name;
this.age = age;
this.id = id;
}
public Worker() {
super();
// TODO Auto-generated constructor stub
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((id == null) ? 0 : id.hashCode());
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;
Worker other = (Worker) obj;
if (age != other.age)
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Worker [name=" + name + ", age=" + age + ", id=" + id + "]";
} //如何重写hashCode?尽量让所有的成员变量都参与到运算中.
//name age id
/*@Override
public int hashCode() {
int code1 = name.hashCode();
int code2 = id.hashCode();
return code1 * 3 + age + code2;
}*/ } public class HashSetDemo2 { public static void main(String[] args) {
HashSet<Worker> set = new HashSet<>(); Worker w1 = new Worker("tom1", 20, "001");
Worker w2 = new Worker("tom1", 20, "001");
Worker w3 = new Worker("tom2", 22, "003");
Worker w4 = new Worker("tom2", 22, "003");
Worker w5 = new Worker("tom3", 22, "003");// set.add(w1);
set.add(w2);
set.add(w3);
set.add(w4);
set.add(w5); for (Worker worker : set) {
System.out.println(worker);
} } }

重写HashCode和equals方法

HashSet注意事项:
1.想要往HashSet中添加的对象,需要在定义类时,重写hashCode和equals方法
2.由于HashSet使用的是散列算法,所以,轻易不要在迭代集合元素的时候改变集合中的元素

2.5并发修改异常

import java.util.HashSet;
import java.util.Iterator; /*
* 演示HashSet并发修改异常
*/
public class HashSetDemo3 { public static void main(String[] args) {
HashSet<String> set = new HashSet<String>(); set.add("hello");
set.add("hello2");
set.add("world");
set.add("world"); Iterator<String> it = set.iterator();
while(it.hasNext()){
String str = it.next();
if(str.equals("world")){
// set.remove("world");//ConcurrentModificationException
it.remove();
}
} for (String s : set) {
System.out.println(s);
} } }

并发修改异常

补充技能:

3、LinkedHashSet类

从后缀可以看出:其本质是HashSet,只不过在内部维护了一个链表,可以记住元素放入的顺序,这样就保证了存取的顺序,

但是正是由于多了链表,所以它的效率低些.

  • 如何保证元素唯一性:hashCode, equals方法
  • 如何保证存取顺序性:链表

没有特殊成员方法全部都是继承而来的。

4、TreeSet类

从图中可以看出:
TreeSet继承于AbstractSet,并且实现了NavigableSet接口。
TreeSet的本质是一个"有序的,并且没有重复元素"的集合,它是通过TreeMap实现的(见构造方法)。TreeSet中含有一个"NavigableMap类型的成员变量"m,而m实际上是"TreeMap的实例"。

4.1成员变量

4.2构造方法

public TreeSet() {
this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}

4.3TreeSet排序

public static void demoOne() {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("张三", 11));
ts.add(new Person("李四", 12));
ts.add(new Person("王五", 15));
ts.add(new Person("赵六", 21)); System.out.println(ts);
}

为什么会报错

案例String类:

匿名内部类改进:

import java.util.Comparator;
import java.util.TreeSet; /*
* TreeSet对元素排序的原理:
* 1.让元素具有比较性
* 2.集合本身具有比较性
*
* 取决于创建集合对象时使用的构造方法.
*
*
*/ class Student /* implements Comparable<Student> */ { private String name;
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public Student(String name, int age) {
super();
this.name = name;
this.age = age;
} public Student() {
super();
// TODO Auto-generated constructor stub
}
/*
* @Override public int compareTo(Student o) { // 首要条件:按照年龄比较 int r1 = -(age
* - o.getAge()); //次要条件:名字,它已经实现了Comparable接口 int r2 = (r1 == 0)?
* -(name.compareTo(o.getName())) : r1; return r2; }
*/
}
/*
class MyComparator implements Comparator<Student> { @Override
public int compare(Student o1, Student o2) {
// o1--> this o2 --> other int r1 = o1.getAge() - o2.getAge();
int r2 = (r1 == 0) ? o1.getName().compareTo(o2.getName()) : r1;
return r2;
} }
*/ public class TreeSetDemo1 { public static void main(String[] args) {
/*
* // 没有传参,意味着使用元素本身的比较性. TreeSet<Student> ts = new TreeSet<Student>();
*
* Student s1 = new Student("tom2", 12); Student s2 = new
* Student("tom3", 13); Student s3 = new Student("tom2", 13); Student s4
* = new Student("tom1", 12);
*
* ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4);
*
* // for (Student student : ts) { System.out.println(student.getName()
* + "--" + student.getAge()); }
*
*/ // 让集合具有比较性
// TreeSet<Student> ts = new TreeSet<>(new MyComparator());
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// o1--> this o2 --> other int r1 = o1.getAge() - o2.getAge();
int r2 = (r1 == 0) ? o1.getName().compareTo(o2.getName()) : r1;
return r2;
} }); Student s1 = new Student("tom2", 12);
Student s2 = new Student("tom3", 13);
Student s3 = new Student("tom2", 13);
Student s4 = new Student("tom1", 12); ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4); for (Student s : ts) {
System.out.println(s.getName() + "--" + s.getAge());
} } }

上面两个案例的代码

练习:

从键盘上录入3个学生的信息,包括语文,数学,英语的成绩三个成员变量,并根据总成绩进行排序

public class Student implements Comparable<Student>{
private String name;
private int ch;
private int math;
private int en; public Student(String name, int ch, int math, int en) {
super();
this.name = name;
this.ch = ch;
this.math = math;
this.en = en;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCh() {
return ch;
}
public void setCh(int ch) {
this.ch = ch;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEn() {
return en;
}
public void setEn(int en) {
this.en = en;
} public int getSum() {
return ch + math + en;
} @Override
public int compareTo(Student s) {
//首要条件
int r1 = getSum() - s.getSum();
//次要条件:语文成绩:
int r2 = (r1 == 0)?getCh()-s.getCh():r1;
//次次要条件:数学成绩:
int r3 = (r2 == 0)?getMath() - s.getMath():r2;
//次要条件:名字
int r4 = (r3 == 0)?getName().compareTo(s.getName()):r3;
return r4;
} }

Student 类

import java.util.Scanner;
import java.util.TreeSet;
public class Test{ public static void main(String[] args) {
TreeSet<Student> set = new TreeSet<Student>(); Scanner sc = new Scanner(System.in);
for(int i = 1;i<4;i++){
System.out.print("输入第" + i + "个学生姓名 : ");
String name = sc.next();
System.out.print("输入第" + i + "个学生语文成绩 : ");
int ch = sc.nextInt();
System.out.print("输入第" + i + "个学生数学成绩 : ");
int math = sc.nextInt();
System.out.print("输入第" + i + "个学生英语成绩 : ");
int en = sc.nextInt(); Student stu = new Student(name, ch, math, en); set.add(stu);
} //
for (Student s : set) {
System.out.println(s.getSum() +","+ s.getName() +","+ s.getCh() +","+ s.getMath() +","+ s.getEn());
} } }

Test

4.4顺序遍历

Iterator顺序遍历

for(Iterator iter = set.iterator(); iter.hasNext(); ) {
iter.next();
}

Iterator顺序遍历

// 假设set是TreeSet对象
for(Iterator iter = set.descendingIterator(); iter.hasNext(); ) {
iter.next();
}

for-each遍历HashSet

// 假设set是TreeSet对象,并且set中元素是String类型
String[] arr = (String[])set.toArray(new String[0]);
for (String str:arr)
System.out.printf("for each : %s\n", str);

TreeSet不支持快速随机遍历,只能通过迭代器进行遍历!

4.5 方法的源码研究

以后写吧

java数据结构4--集合Set的更多相关文章

  1. java 16 - 5 LinkedList模拟栈数据结构的集合

    请用LinkedList模拟栈数据结构的集合,并测试 题目的意思是: 你自己的定义一个集合类,在这个集合类内部可以使用LinkedList模拟. package cn_LinkedList; impo ...

  2. Java基础知识强化之集合框架笔记29:使用LinkedList实现栈数据结构的集合代码(面试题)

    1. 请用LinkedList模拟栈数据结构的集合,并测试:  题目的意思是:     你自己的定义一个集合类,在这个集合类内部可以使用LinkedList模拟,使用LinkedList功能方法封装成 ...

  3. (7)Java数据结构--集合map,set,list详解

    MAP,SET,LIST,等JAVA中集合解析(了解) - clam_clam的专栏 - CSDN博---有颜色, http://blog.csdn.net/clam_clam/article/det ...

  4. Java基础学习笔记(六) - 数据结构和集合

    一.认识数据结构 1.数据结构有什么用? 合理的使用数据结构,可以更方便的查找存储数据. 2.常见的数据结构 数据存储常用结构有:栈.队列.数组.链表和红黑树. 栈:堆栈(stack),它是运算受限的 ...

  5. Java数据结构之树和二叉树(2)

    从这里始将要继续进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来 ...

  6. Java数据结构之树和二叉树

    从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...

  7. Java基础--说集合框架

    版权所有,转载注明出处. 1,Java中,集合是什么?为什么会出现? 根据数学的定义,集合是一个元素或多个元素的构成,即集合一个装有元素的容器. Java中已经有数组这一装有元素的容器,为什么还要新建 ...

  8. Clojure学习03:数据结构(集合)

    Clojure提供了几种强大的数据结构(集合) 一.集合种类 1.vector 相当于数组,如: [2  3   5]  ,  ["ad"  "adas"  & ...

  9. java 数据结构 图

    以下内容主要来自大话数据结构之中,部分内容参考互联网中其他前辈的博客,主要是在自己理解的基础上进行记录. 图的定义 图是由顶点的有穷非空集合和顶点之间边的集合组成,通过表示为G(V,E),其中,G标示 ...

  10. Java数据结构整理(一)

    ava数据结构内容整理关键字: 数据结构 Collection:List.SetMap:HashMap.HashTable如何在它们之间选择一.Array , ArraysJava所有“存储及随机访问 ...

随机推荐

  1. JavaScript 积累

    1. 基本类型值在内存中占据固定大小的空间,因此被保存在栈空间中: 2. 引用类型的值是对象,保存在堆空间中: 3. 从一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本:从一个变量向另一个 ...

  2. 转载-c++深拷贝和浅拷贝

    转载自:https://blog.csdn.net/u010700335/article/details/39830425 C++中类的拷贝有两种:深拷贝,浅拷贝:当出现类的等号赋值时,即会调用拷贝函 ...

  3. Stream流实现斐波那契数列

    1.前言 我们都知道斐波那契数列有很多种实现方法,在jdk1.8以前没有流操作,只能通过递归或者迭代等其他方式来实现斐波那契数列, 但是jdk1.8以后,有了流操作,我们就可以使用流来实现斐波那契数列 ...

  4. python3实现自动化框架robotframework(最新)

    # python3.6及以上版本兼容RIDE1.7.3版本由于最近RIDE1.7.3的版本改进,RIDE这个版本对高版本的wxpython兼容很好,python3.6及以上版本都可以顺利运行RIDE为 ...

  5. Python学习之数据库

    9.6 表的查询 [结构]select distinct 字段1,字段2 from 表名 where 条件 group by 字段 having 筛选 order by 字段 limit 限制条数 [ ...

  6. XSS-存储型

    @实操视频https://www.bilibili.com/video/av26679456?from=search&seid=13377211289924067562 存储型的注入对象不是搜 ...

  7. Magic Potion(网络流)

    原题链接 2018南京的铜牌题,听说学长他们上来就A了,我这个图论选手也就上手做了做,结果一言难尽...... 发此篇博客希望自己能牢记自己的菜... 本题大意:有n个heros和m个monsters ...

  8. Linux运维必会的100道MySql面试题之(一)

    01 如何启动MySql服务 /etc/init.d/mysqld start service mysqld start Centos 7.x 系统 sysctl  start mysqld 02 检 ...

  9. 【网络安全】window 快速搭建 ftp 及 多种访问方式

    在局域网里面使用ftp传输文件比使用qq等软件传输速度快很多,但是搭建ftp很多时候需要下载相应的支持软件,其实不必下载相关的软件,因为window自带ftp功能. 演示操作系统:windows10 ...

  10. Action获取请求参数的3中方式

    方式一:Action本身作为Model对象,通过属性注入(Setter)方法讲参数数据封装到Action中 具体为:在Action中,提供和参数名相同的几个属性,并为其提供set方法,那么,该参数会被 ...