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. Several ports (8005, 8080, 8009) required by Tomcat v8.5 Server at localhost are already in use.

    Several ports (8005, 8080, 8009) required by Tomcat v8.5 Server at localhost are already in use. The ...

  2. js在页面中添加一个元素 —— 添加弹幕

    参考地址 [往下拉 —— 使用HTML DOM appendChild() 方法实现元素的添加 ] 一.创建 HTML <div class="right_liuyan"&g ...

  3. unity快捷放置物体操作

    https://connect.unity.com/p/zui-jia-shi-jian-dui-xiang-fang-zhi-he-wu-li-xiao-guo 最佳实践系列文章将探讨我们在与客户合 ...

  4. 转·带你用实例理解C语言回调函数

    原文出处:https://segmentfault.com/a/1190000008293902?utm_source=tag-newest 前言: 如不懂函数指针,请先查阅关于函数指针内容的资料(h ...

  5. 【VS开发】ActiveX开发注意事项

    [VS开发]ActiveX开发注意事项 标签:[VS开发] 注意:必须在工程的app文件的InitInstance()中加入如下代码,否则动态创建控件不会成功: AfxEnableControlCon ...

  6. [JavaScript] console.log只在查看时才会读取这个打印的对象,并把此刻相关属性和值显示出来

      /** * 写个函数解决console.log只在查看时才会读取这个打印的对象,并把此刻相关属性和值显示出来 * @param arg */ const log = function (...ar ...

  7. Jmeter 04 Jmeter变量的使用

    在使用jmeter进行接口测试时,我们难免会遇到需要从上下文中获取测试数据的情况,这个时候就需要引入变量了. 定义变量 添加->配置元件->用户自定义的变量 添加->配置元件-> ...

  8. js if(!!!e) {} 判断条件中的三个感叹号什么意思

    两个感叹号的意思就是,将变量转换为其对应的布尔值. !!e就是e对应的布尔值,true或者false. !!!e==!(!!e)==!true/!false=false/true;

  9. SQLSERVER 秘钥整理

    SQLSERVER 2017 Enterprise Core 6GPYM-VHN83-PHDM2-Q9T2R-KBV83 Developer ---- Enterprise TDKQD-PKV44-P ...

  10. C++中组合和继承的概念及意义

    1,继承在面向对象中具有举足轻重的地位,面向对象当中的很多高级技术都和继承是息息相关的,比如面向对象的高端课程<设计模式>中的每一种技术都和继承有关,因此我们非常有必要在学习 C++ 时, ...