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. 关于springmvc的一些注解详解

    引言: 前段时间项目中用到了RESTful模式来开发程序,但是当用POST.PUT模式提交数据时,发现服务器端接受不到提交的数据(服务器端参数绑定没有加任何注解),查看了提交方式为applicatio ...

  2. Java Stream流排序null以及获取指定条数数据

    Java8的Stream流的一些用法, //排序 carerVehEntityList = carerVehEntityList.stream().sorted( Comparator.compari ...

  3. RTX数据表分析

    /******************************************* * UserName 做主键 **************************************** ...

  4. java:(监听,上传,下载)

    1.监听: index.jsp: <%@ page language="java" import="java.util.*" pageEncoding=& ...

  5. Pycharm最新激活码汇总,pycharm2019激活码

    Pycharm激活码汇总 激活过程如下: 1.双击运行桌面上的Pycharm图标,进入下图界面,选择Do not import settings,之后选择OK,进入下一步. 2.拖动到底部,选择Acc ...

  6. Python Basics with Numpy

    Welcome to your first assignment. This exercise gives you a brief introduction to Python. Even if yo ...

  7. python 列表的(总结)

    列表(自我总结) 1.在python中什么是列表 列:排列,表:一排数据 在python中的表达就是 l = [1,2,3,4,5,6,7] 2.列表是可变类型还是不可变类型 也就是说列表能不能被ha ...

  8. 使用graphics.h来绘制图形

    |   版权声明:本文为博主原创文章,未经博主允许不得转载. graphics.h是TC里面的图形库,如果要用的话应该用TC来编译.分为:像素函数.直线和线型函数.多边形函数.填充函数等.然而在我们使 ...

  9. Boruvka

    大概是这样的:一开始图中有\(n\)个连通块,每次操作我们选出各个连通块连出去的最短的边(如果有相同边权的边的话可以把序号作为第二关键字),然后把这些边加入最小生成树. 最坏的情况下每次操作都会让当前 ...

  10. springboot2.0自适应效果错误响应

    实现效果当访问thymeleaf渲染页面时,显示的是自定义的错误页面 当以接口方式访问时,显示的是自定义的json数据响应 1. 编写自定义异常 package cn.jfjb.crud.except ...