Set接口

Java Set中任取一个元素 java set怎么取数据_java set的数据结构

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_02

Java Set中任取一个元素 java set怎么取数据_java_03

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

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

重复元素的理解:

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

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

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

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

1 Set接口的方法

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_04

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

2、HashSet类

线程不安全,存取速度快

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

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

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

Java Set中任取一个元素 java set怎么取数据_java_05

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

Java Set中任取一个元素 java set怎么取数据_ide_06

2.2成员变量

Java Set中任取一个元素 java set怎么取数据_Java Set中任取一个元素_07

2.3成员方法

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_08

Java Set中任取一个元素 java set怎么取数据_java_09

2.4HashSet如何保证元素唯一?

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

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

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

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

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

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

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

Java Set中任取一个元素 java set怎么取数据_Java Set中任取一个元素_10

Java Set中任取一个元素 java set怎么取数据_System_11

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

Java Set中任取一个元素 java set怎么取数据_java_12

2.5并发修改异常

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

并发修改异常

补充技能:

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_13

3、LinkedHashSet类

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

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

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

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

Java Set中任取一个元素 java set怎么取数据_ide_14

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

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_15

4、TreeSet类

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_16

Java Set中任取一个元素 java set怎么取数据_System_17

Java Set中任取一个元素 java set怎么取数据_System_18

从图中可以看出:

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

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

4.1成员变量

Java Set中任取一个元素 java set怎么取数据_Java Set中任取一个元素_19

4.2构造方法

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_20

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排序

Java Set中任取一个元素 java set怎么取数据_Java Set中任取一个元素_21

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

为什么会报错

Java Set中任取一个元素 java set怎么取数据_java_22

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_23

Java Set中任取一个元素 java set怎么取数据_Java Set中任取一个元素_24

Java Set中任取一个元素 java set怎么取数据_java_25

Java Set中任取一个元素 java set怎么取数据_System_26

Java Set中任取一个元素 java set怎么取数据_System_27

Java Set中任取一个元素 java set怎么取数据_java_28

Java Set中任取一个元素 java set怎么取数据_Java Set中任取一个元素_29

Java Set中任取一个元素 java set怎么取数据_System_30

Java Set中任取一个元素 java set怎么取数据_java_31

案例String类:

Java Set中任取一个元素 java set怎么取数据_java_32

Java Set中任取一个元素 java set怎么取数据_System_33

Java Set中任取一个元素 java set怎么取数据_System_34

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_35

Java Set中任取一个元素 java set怎么取数据_java_36

Java Set中任取一个元素 java set怎么取数据_java set的数据结构_37

Java Set中任取一个元素 java set怎么取数据_Java Set中任取一个元素_38

匿名内部类改进:

Java Set中任取一个元素 java set怎么取数据_java_39

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

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 类

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 方法的源码研究

以后写吧