集合Set
1.Set
常用方法:直接查看api,里面的方法我们基本都是学过的
Set的遍历 1:Iterator,2:foreach
2.HashSet(查询api看说明进行讲解)
2.1 元素顺序:元素唯一,但是无序(它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变)
2.2如何保证元素的唯一性的呢(分析源码)?
通过简单的分析,我们知道HashSet集合保证元素的唯一性和add()方法相关。
如何我们想深入的了解,就必须看add()方法的源码,看它的底层依赖什么内容?
// if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {...}
//
// 左边:e.hash == hash
// 比较对象的哈希值。
//
// 右边:((k = e.key) == key || key.equals(k))
// 左边:(k = e.key) == key
// 比较对象的地址值。
//
// 右边:key.equals(k)
// 比较的是对象的内容是否相同。默认情况下比较的是地址值
结论:
底层数据结构是哈希表。
哈希表依赖两个方法:hashCode()和equals()
执行流程:
首先判断哈希值是否相同,如果不同,就直接添加到集合。
如果相同,继续执行equals(),看其返回值,
如果是false,就直接添加到集合。
如果是true,说明元素重复不添加。
使用注意事项:如果你看到哈希结构的集合,就要考虑可能需要重写这两个方法。add()方法,保证唯一性的原理(底层依赖于hashCode()和equals()方法,保证了元素的唯一性)。
package com.edu.study7;
//需求:举例说明元素唯一性
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
/**HashSet集合
* 元素唯一,但是无序
*
* 基本数据类型包装类以及String类中已经重写过hashCode()和equals()方法
* 所以自动保证了元素唯一性
*/
//创建HashSet集合
HashSet<String> hashSet = new HashSet<String>();
//给集合添加元素
hashSet.add("hello");
hashSet.add("world");
hashSet.add("java");
hashSet.add("hello");
hashSet.add("php");
hashSet.add("c");
hashSet.add("你好哦");
hashSet.add("heihei");
//遍历
for (String s : hashSet) {
System.out.println(s);
}
}
}
//第一次运行结果: 第八次运行结果:
// hello hello
// heihei c
// c heihei
// php php
// 你好哦 你好哦
// java java
// world world
需求:自定义学生对象,保证 集合元素唯一性
package com.edu.study7;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
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;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
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;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
package com.edu.study7;
//HashSet集合,自定义类中必须重写hashCode()和equals()方法,才可以保证元素唯一性
import java.util.HashSet;
import java.util.Iterator;
public class HashSetTest {
public static void main(String[] args) {
//创建HashSet集合
HashSet<Student> hashSet = new HashSet<Student>();
//创建学生对象
Student s1 = new Student("张三",13);
Student s2 = new Student("李四",13);
Student s3 = new Student("王五",13);
Student s4 = new Student("张三",13);
//给集合添加元素
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
hashSet.add(s4);
//遍历
Iterator<Student> it = hashSet.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s.getName()+" "+s.getAge());
}
}
}
//不重写输出结果: 重写:
// 李四 13 李四 13
// 张三 13 张三 13
// 王五 13 王五 13
// 张三 13
3.TreeSet(查看api,得出结论)
3.1 元素顺序:使用元素的自然顺序对元素进行排序,或者根据创建 set时提供的 Comparator进行排序(比较器排序),具体取决于使用的构造方法。
3.2 TreeSet集合的底层算法:二叉树
package com.edu.study8;
/**
* 基本数据类包装类以及String类都实现Comparable接口
* 且都重写了compareTo()方法
*/
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//创建TreeSet集合
/** TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add("hello");
treeSet.add("world");
treeSet.add("c");
treeSet.add("java");
//遍历
for (String string : treeSet) {
System.out.println(string);
}
//输出:
// c
// hello
// java
// world
*/
TreeSet<Integer> ts = new TreeSet<Integer>();
ts.add(12);
ts.add(25);
ts.add(89);
ts.add(3);
//遍历
for (Integer i : ts) {
System.out.println(i);
}
//输出结果:
// 3
// 12
// 25
// 89
}
}
3.3 元素要求, 加入自定义JavaBean
需求:存入学生对象(姓名,年龄),1.按年龄排序,2.姓名排序(自然排序实现Comparable接口,并重写comparaTo()方法)
package com.edu_04;
//新建Student类
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
//实现Comparable接口的同时必须实现这个比较法
@Override
public int compareTo(Student s) {
//就是写的是元素的比较规则,由你自己去动手写出
//按照学生的年龄进行排序
/**
* 两个对象进行比较:
* s
* this
*/
int num = this.age - s.age;
//判断年龄是否相同,如果相同比较姓名
/**
* 写这个比较规则的时候注意两点:
* 1.他有主要条件,先按照主要条件进行排序
* 2.如果主要条件相同,就需要你自己分析出来他的次要条件,再去按照次要条件进行比较
*/
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
package com.edu_04;
import java.util.TreeSet;
/**
* java.lang.ClassCastException:类型转换异常
* TreeSet集合有两种排序方式:至于哪两种排序方式,取决于他的构造器
* 自然排序:无参构造public TreeSet()
*/
public class TreeSetDemo {
public static void main(String[] args) {
//创建TreeSet集合,存储自定义对象
TreeSet<Student> ts = new TreeSet<Student>();
//给集合中添加Student对象
Student s = new Student("guodegang", 50);
Student s6 = new Student("liuyifei", 50);
Student s2 = new Student("zhangxueyou", 55);
Student s3 = new Student("amu", 45);
Student s4 = new Student("tf-boys", 18);
Student s5 = new Student("wangfeng", 49);
ts.add(s);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历集合
for (Student student : ts) {
System.out.println(student);
}
}
}
需求:创建set集合的时候,传入Comparator(比较器排序)进行排序,进行排序(比较器排序)
package com.edu_05;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//使用匿名内部类
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num==0?s1.getName().compareTo(s2.getName()):num;
return num2;
}
});
//创建对象存入集合
Student s = new Student("guodegang", 50);
Student s6 = new Student("liuyifei", 50);
Student s2 = new Student("zhangxueyou", 55);
Student s3 = new Student("amu", 45);
Student s4 = new Student("tf-boys", 18);
Student s5 = new Student("wangfeng", 49);
ts.add(s);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历集合
for (Student student : ts) {
System.out.println(student);
}
}
}
4. HashSet与TreeSet的相同点与不同点
相同点:
单列集合,元素不可重复
不同点
1. 底层存储的数据结构不同
HashSet底层用的是HashMap哈希表结构存储,而TreeSet底层用的是TreeMap树结构存储
2.存储时保证数据唯一性依据不同
HashSet是通过复写hashCode()方法和equals()方法来保证的,而TreeSet通过Compareable接口的compareTo()方法来保证的
3.有序性不一样
HashSet无序,TreeSet有序
集合Map
一 一对应的映射关系。这种关系的集合在java叫Map。
Map:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
1. Map接口中的方法:
A:删除功能
void clear():移除集合中的所有键值对元素
V remove(Object key):根据键移除键值对元素,并返回值
B:判断功能
boolean containsKey(Object key):判断集合中是否包含指定的键
boolean containsValue(Object value):判断集合中是否包含指定的值
boolean isEmpty():判断集合是否为空
C:获取功能
Set<Map.Entry<K,V>> entrySet():获取键值对对象的集合,遍历键值对对象,利用getKey(),getValue()取出键和值
V get(Object key):根据键获取值
Set<k> keySet():获取所有的键
Collection<v> values():获取所有的值
D:添加功能
V put(K key,V value):集合添加键值对
E:长度功能
int size():键值对对数。
2. HashMap
2.1元素顺序:元素顺序不可预测
2.2底层算法:哈希算法
2.3对键没有要求(仅仅相对于TreeMap来说)
package com.edu.study12;
//练习:存入(String,String)进行练习
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
//创建HashMap集合
HashMap<String, String> hashMap = new HashMap<String, String>();
//给集合添加元素
hashMap.put("A", "a");
hashMap.put("B", "b");
hashMap.put("C", "c");
hashMap.put("D", "d");
hashMap.put("A", "e");
//当后面存入的元素和前面的键的值相同的时候,前面的元素的值会被后面的元素的值代替
Set<String> keySet = hashMap.keySet();
for (String key : keySet) {
System.out.println(key+" "+hashMap.get(key));
}
}
}
// D d
// A e
// B b
// C c
练习 :
package com.edu.study13;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
package com.edu.study13;
//练习:存入(String,Student)
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
//创建HashMap集合
HashMap<String, Student> hashMap = new HashMap<String, Student>();
//创建Student对象
Student s1 = new Student("zhang",6);
Student s2 = new Student("li",25);
Student s3 = new Student("sun",17);
Student s4 = new Student("zhang",6);
//给集合中添加元素
hashMap.put("中国", s1);
hashMap.put("韩国", s2);
hashMap.put("美国", s3);
hashMap.put("日本", s4);
//当后面存入的元素和前面的键的值相同的时候,前面的元素的值会被后面的元素的值代替
//通过键找值
Set<String> keySet = hashMap.keySet();
//得到的Set本质上是HashSet,元素唯一,无序
//而键为String类型,已经重写了hashCode()和equals()方法,故保证了唯一性
for (String key : keySet) {//因为在Student类中重写toString()方法
System.out.println(key+" "+hashMap.get(key));
}
}
}
//日本 Student [name=zhang, age=6]
//韩国 Student [name=li, age=25]
//美国 Student [name=sun, age=17]
//中国 Student [name=zhang, age=6]
练习:
package com.edu.study14;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
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;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
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;
Student other = (Student) obj;
if (age != other.age)
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 "Student [name=" + name + ", age=" + age + "]";
}
}
package com.edu.study14;
//传入(Student, String),进行练习
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
//创建HashMap集合
HashMap<Student, String> hashMap = new HashMap<Student, String>();
//创建Student对象
Student s1 = new Student("zhang",6);
Student s2 = new Student("liu",13);
Student s3 = new Student("sun",20);
Student s4 = new Student("zhang",6);
//给集合添加元素
hashMap.put(s1, "美国");
hashMap.put(s2, "中国");
hashMap.put(s3, "韩国");
hashMap.put(s4, "中国");
//遍历
Set<Student> keySet = hashMap.keySet();
//此时的Set实质上是HashSet,元素唯一,无序
//而键是Student类对象,应重写hashCode()和equals()方法,方可保证唯一性
//在Student类中重写hashCode()和equals()方法
for (Student key : keySet) {
//在Student类中重写toString()方法
System.out.println(key+" "+hashMap.get(key));
}
}
}
// Student [name=liu, age=13] 中国
// Student [name=sun, age=20] 韩国
// Student [name=zhang, age=6] 中国
3. Treemap
3.1元素顺序:元素顺序与键的排序规则有关
3.2底层算法:Tree算法
4. 遍历
集合遍历:①获取建的集合 ②遍历键 ③根据键找值foreach()
1.foreach():根据丈夫找妻子(根据键找值)
2.entrySet():(Set<Map.Entry<K,V>> entrySet())先找到夫妻的结婚证,再从结婚证里面找到丈夫和妻子(先找到键值对对象,再从键值对对象里面找到键和值)
练习:存入(Integer,String)
package com.edu_11;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
//1.存入(Integer,String),主要验证排序方式
//c创建TreeMap集合
TreeMap<Integer, String> tm = new TreeMap<Integer, String>();
//存入元素
tm.put(1, "赵");
tm.put(4, "刘");
tm.put(2, "坤");
tm.put(3, "李");
//遍历集合
Set<Integer> keys = tm.keySet();
for (Integer key : keys) {
System.out.println(key+" "+tm.get(key));
}
}
}
/** 1 赵
2 坤
3 李
4 刘
*/
练习:存入(Student,String)
新建学生类
package com.edu_12;
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
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;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Student s) {
//先按年龄排序,年龄相同按照姓名排序
//int num = this.age - s.age;
int num =s.age - this.age;
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
}
测试
package com.edu_12;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
// 2.存入(Student,String),键:Student 值:地址
//创建TreeMap集合
TreeMap<Student, String> tm = new TreeMap<>();
//创建学生对相同
Student s1 = new Student("郭德纲", 50);
Student s2 = new Student("刘德华", 55);
Student s3 = new Student("张学友", 58);
Student s4 = new Student("黎明", 40);
Student s5 = new Student("郭德纲", 50);
//将元素存入集合
tm.put(s1, "中国");
tm.put(s2, "中国");
tm.put(s3, "中国");
tm.put(s4, "中国");
tm.put(s5, "中国");
//遍历集合
Set<Student> keys = tm.keySet();
for (Student s : keys) {
System.out.println(s+" "+tm.get(s));
}
}
}
/** Student [name=张学友, age=58] 中国
Student [name=刘德华, age=55] 中国
Student [name=郭德纲, age=50] 中国
Student [name=黎明, age=40] 中国
*/
5. HashMap与TreeMap的相同点与不同点
相同点:
主要用于存储键(key)值(value)对,根据键得到值,因此键不允许键重复,但允许值重复。
不同点:
1.HashMap里面存入的键值对在取出的时候是随机的,也是我们最常用的一个Map.根据键可以直接获取它的值,具有很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。
2.TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。