java set的数据结构_java数据结构4--集合Set

Set接口

5328eb4d291643de8b5efb0b0e6eaccd.png

c1ea6a9da48905b3891f8b8392604969.png

256b89ad956e7a514992519a641e1148.png

Set接口用来表示:一个不包含“重复元素”的集合

Set接口中并没有定义特殊的方法,其方法多数都和Collection接口相同。

重复元素的理解:

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

HashSet中对重复元素的理解:和通常意义上的理解不太一样!

两个元素(对象)的hashCode返回值相同,并且equals返回值为true时(或者地址相同时),才称这两个元素是相同的。

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

1 Set接口的方法

24e1e592d640354f5d73c4a90470a7e2.png

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

2、HashSet类

线程不安全,存取速度快

它的大多数方法都和Collection相同

它不保证元素的迭代顺序;也不保证该顺序恒久不变

当HashSet中的元素超过一定数量时,会发生元素的顺序重新分配。

ddd9bc352d103605d8e953d9e9a13fa7.png

2.1HashSet构造方法

publicHashSet() {

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, floatloadFactor) {

map= new HashMap<>(initialCapacity, loadFactor);

}public HashSet(intinitialCapacity) {

map= new HashMap<>(initialCapacity);

}

HashSet(int initialCapacity, float loadFactor, booleandummy) {

map= new LinkedHashMap<>(initialCapacity, loadFactor);

}

看来set底层是HashMap

e134f9c57237dac754eefb6f23fd6b61.png

2.2成员变量

782a47a2ef470922a6e3ca898121c56c.png

2.3成员方法

626885011f5d06e979903b8afa0694c0.png

2020be8fa00696ff9d330ac8cce65ff2.png

2.4HashSet如何保证元素唯一?

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

先调用obj的hashCode方法,计算哈希值(槽位值slot:bucket)

根据哈希值确定存放的位置

若位置上没有元素,则这个元素就是第一个元素,直接添加

若此位置上已经有元素,说明还有元素的hashCode方法返回值与它相同,则调用它的equals方法与已经存在的元素进行比较

若返回值为true,表明两个元素是“相同”的元素,不能添加

若返回值为false,表明两个元素是“不同”的元素,新元素将以链表的形式添加到集合中

63cb111ec8d5b145b14aaf5408b70e55.png

01d4239ae61b402c22255c0a00f32957.png

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

importjava.util.HashSet;/**自定义对象存储到HashSet中

*

*int hashCode:元素被添加时被调用,用于确认元素的槽位值

*boolean equals:当发生碰撞时,调用被添加元素的equals方法和已经存在的元素进行比较,

* true:不能添加

* false:可以添加,多个元素占用一个槽位值.以链表形式存在.

**/

classWorker{//static int i = 0;

privateString name;private intage;privateString id;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}public intgetAge() {returnage;

}public void setAge(intage) {this.age =age;

}publicString getId() {returnid;

}public voidsetId(String id) {this.id =id;

}public Worker(String name, intage, String id) {super();this.name =name;this.age =age;this.id =id;

}publicWorker() {super();//TODO Auto-generated constructor stub

}

@Overridepublic inthashCode() {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());returnresult;

}

@Overridepublic booleanequals(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;

}

@OverridepublicString 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 classHashSetDemo2 {public static voidmain(String[] args) {

HashSet 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使用的是散列算法,所以,轻易不要在迭代集合元素的时候改变集合中的元素

643183d86ce279b257283ee25201c3b2.png

2.5并发修改异常

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

importjava.util.HashSet;importjava.util.Iterator;/** 演示HashSet并发修改异常*/

public classHashSetDemo3 {public static voidmain(String[] args) {

HashSet set = new HashSet();

set.add("hello");

set.add("hello2");

set.add("world");

set.add("world");

Iterator 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);

}

}

}

并发修改异常

补充技能:

189891353b8a63cfb2c28eb66b40eb5a.png

3、LinkedHashSet类

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

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

如何保证元素唯一性:hashCode, equals方法

如何保证存取顺序性:链表

f0c171ef70286927643ac6d8c89a6240.png

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

9691a7a38a141b06b8e8778298a26c37.png

4、TreeSet类

9243c12ab552352248d6753ce51366dc.png

38c74c1988e29b7effb279672a119347.png

83505c11e2d7d2336ff1bf7043b5f36c.png

从图中可以看出:

TreeSet继承于AbstractSet,并且实现了NavigableSet接口。

TreeSet的本质是一个"有序的,并且没有重复元素"的集合,它是通过TreeMap实现的(见构造方法)。TreeSet中含有一个"NavigableMap类型的成员变量"m,而m实际上是"TreeMap的实例"。

4.1成员变量

592cbff9a9b0d81370e5c64e1dea2a5f.png

4.2构造方法

6eb119a0cb45366a258eb9582f74446e.png

publicTreeSet() {this(new TreeMap());

}public TreeSet(Comparator super E>comparator) {this(new TreeMap<>(comparator));

}public TreeSet(Collection extends E>c) {this();

addAll(c);

}public TreeSet(SortedSets) {this(s.comparator());

addAll(s);

}

4.3TreeSet排序

00abb88d74e14f6df76ec90c2b13b06c.png

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public static voiddemoOne() {

TreeSet 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);

}

为什么会报错

7cf2dd1927b503226f026b82e74c5cf7.png

1afdcdee126517acfe2c55091a18c9b6.png

d602db393b4980489cd6d5d6aaf43c4d.png

15f359825e4df290fd9a05f24fb3c9b9.png

8aa3bd625c2a55358c92b673b1d9e301.png

b348c03f1752c722da120d69c63e6d66.png

6eb79b83a81b96acb5e5d94c01a5686c.png

a9cbc14bc3981ed4cbe3af9f932ca7d5.png

160e728015c0e5011bb4fef27d602313.png

2e7ddaa67471bd08fd3ab713874c8528.png

案例String类:

4f538598de09da8413a0cf06a54ec3a0.png

cb343f289631ef7acb2d7e7986359708.png

a47dcc4df916a0c8d02cc4301a60d4fd.png

671426c854d90e22b872057d446f8bd7.png

35feb9c97fab8e461504abee14929b0a.png

2a5b969507bfb8b08ad138560dded883.png

ac63fbc49d27cb76ede91eb7118ff1ad.png

匿名内部类改进:

8b98a0182f6734fe01e9cc4d830f18c1.png

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

importjava.util.Comparator;importjava.util.TreeSet;/** TreeSet对元素排序的原理:

* 1.让元素具有比较性

* 2.集合本身具有比较性

*

* 取决于创建集合对象时使用的构造方法.

*

**/

class Student /*implements Comparable*/{privateString name;private intage;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}public intgetAge() {returnage;

}public void setAge(intage) {this.age =age;

}public Student(String name, intage) {super();this.name =name;this.age =age;

}publicStudent() {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 {

@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 classTreeSetDemo1 {public static voidmain(String[] args) {/** // 没有传参,意味着使用元素本身的比较性. TreeSet ts = new TreeSet();

*

* 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 ts = new TreeSet<>(new MyComparator());

TreeSet ts = new TreeSet<>(new Comparator() {

@Overridepublic intcompare(Student o1, Student o2) {//o1--> this o2 --> other

int r1 = o1.getAge() -o2.getAge();int r2 = (r1 == 0) ?o1.getName().compareTo(o2.getName()) : r1;returnr2;

}

});

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个学生的信息,包括语文,数学,英语的成绩三个成员变量,并根据总成绩进行排序

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public class Student implements Comparable{privateString name;private intch;private intmath;private inten;public Student(String name, int ch, int math, inten) {super();this.name =name;this.ch =ch;this.math =math;this.en =en;

}publicStudent() {super();//TODO Auto-generated constructor stub

}publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}public intgetCh() {returnch;

}public void setCh(intch) {this.ch =ch;

}public intgetMath() {returnmath;

}public void setMath(intmath) {this.math =math;

}public intgetEn() {returnen;

}public void setEn(inten) {this.en =en;

}public intgetSum() {return ch + math +en;

}

@Overridepublic intcompareTo(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;returnr4;

}

}

Student 类

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

importjava.util.Scanner;importjava.util.TreeSet;public classTest{public static voidmain(String[] args) {

TreeSet set = new TreeSet();

Scanner sc= newScanner(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= newStudent(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 方法的源码研究

以后写吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
程序 = 数据结构 + 算法  程序是为了解决实际问题而存在的。然而为了解决问题,必定会使用到某些数据结构以及设计一个解决这种数据结构的算法。如果说各种编程语言是程序员的招式,那么数据结构和算法就相当于程序员的内功。编程实战算法,不是念PPT,我们讲的就是实战与代码实现与企业应用。程序 = 数据结构 + 算法                ——图灵奖得主,计算机科学家N.Wirth(沃斯)作为程序员,我们做机器学习也好,做python开发也好,java开发也好。有一种对所有程序员无一例外的刚需 —— 算法与数据结构日常增删改查 + 粘贴复制 + 搜索引擎可以实现很多东西。同样,这样也是没有任何竞争力的。我们只可以粘贴复制相似度极高的功能,稍复杂的逻辑没有任何办法。语言有很多,开发框架更是日新月异3个月不学就落后我们可以学习很多语言,很多框架,但招聘不会考你用5种语言10种框架实现同一个功能。真正让程序员有区分度,企业招聘万年不变的重点 —— 算法与数据结构。算法代表程序员水平的珠穆朗玛。如果说各种编程语言是程序员的招式,那么数据结构和算法就相当于程序员的内功。 想写出精炼、优秀的代码,不通过不断的锤炼,是很难做到的。 开这个系列的目的是为了自我不断积累。不积跬步无以至千里嘛。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值