Set集合
Set接口:Set集合继承自Collection集合
Set:底层数据结构是一个哈希表,能保证元素是唯一的,元素不重复! 它通过它的子实现了HashSet集合去实例化,HashSet集合底层是HashMap集合的实例! 例. 需求:Set集合存储字符串元素并遍历
public class SetDemo {
public static void main(String[] args) {
//创建Set集合对象
Set<String> set = new HashSet<String>() ;
//添加元素
set.add("hello");
set.add("java") ;
set.add("java") ;
set.add("world") ;
set.add("world") ;
set.add("world") ;
//增强for遍历
for(String s :set){
System.out.println(s);
}
}
}
List集合和Set集合的区别?
List:元素可以重复,有序性(存储和取出一致)
Set集合的子实现类:
1)HashSet:
实现Set接口,依靠Hashtable,他不保证迭代的顺序,特别的是,它不能保证数据保持不变,随着时间的推移。这个集合可以包含null值。存储结构实际为HashMap。
HashSet集合的add()方法,底层是依赖于双列集合HashMap<K,V>的put(K key,V value)来实现的
put(K key,V value):
底层又依赖于HashCode()和equals()方法,传递添加元素的时候,首先判断的是
每一个元素对应的HashCode值是否一样,如果HashCode值一样,还比较他们的equals()方法,由于现在集合存储的是String类型,String类型本身重写
了equals()方法,所以,默认比较的是内容是否相同,如果内容相同,这里最终返回的就是第一次存储的那个元素,由这两个方法保证元素唯一性!
例:使用HashSet集合存储自定义对象并遍历
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
//创建一个HashSet集合对象
HashSet<Student> hs = new HashSet<Student>() ;
//创建学生对象
Student s1 = new Student("高圆圆", 27) ;
Student s2 = new Student("张三", 25) ;
Student s3 = new Student("唐嫣", 26) ;
Student s4 = new Student("邓超", 29) ;
Student s5 = new Student("胡歌", 23) ;
Student s6 = new Student("高圆圆", 27) ;
//给集合中添加学生对象
hs.add(s1) ;
hs.add(s2) ;
hs.add(s3) ;
hs.add(s4) ;
hs.add(s5) ;
hs.add(s6) ;
//增强for遍历
for(Student s : hs){
System.out.println(s.getName()+"---"+s.getAge());
}
/**
* "高圆圆---27" 重复出现是因为现在是自定义对象:在当前自定义对象的类中没有重写两个方法
* hashCode和equals()方法;HashSet底层是依赖于这两个实现来保证元素的唯一性!
*
* */
}
}
HashSet的子类LinkedHashSet集合:
由哈希表保证元素的唯一性
由链接列表来保证元素的有序性!
具有可预知迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序。
public class LinkedHashSetDemo {
public static void main(String[] args) {
//创建LinkedHashSet集合对象
LinkedHashSet<String> link = new LinkedHashSet<String>();
//给集合中添加元素
link.add("hello") ;
link.add("world") ;
link.add("world") ;
link.add("Java") ;
link.add("Java") ;
link.add("JavaWeb") ;
link.add("JavaWeb") ;
//遍历集合
for(String s: link){
System.out.println(s);//元素唯一并且有序
}
}
}
2) TreeSet集合
先了解一下红黑树
红黑树是一种自平衡的二叉查找树 ,那么二叉查找树(BST)具备什么特性呢?
1.左子树上所有结点的值均小于或等于它的根结点的值。
2.右子树上所有结点的值均大于或等于它的根结点的值。
3.左、右子树也分别为二叉排序树。
这种方式是二分查找的思想,当需要查找10这个元素时,查找的次数就是树的高度,但是二叉查找树也存在缺陷,比如
所以红黑树应运而生关于红黑树的规则:
1.节点是红色或黑色。
2.根节点是黑色。
3.每个叶子节点都是黑色的空节点(NIL节点)。
4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
下图中这棵树,就是一颗典型的红黑树:
红黑树可以通过变色 ,旋转来调整被打破的规则
TreeSet集合底层是依赖于TreeMap的实例,而TreeMap<K,V>是依赖于红黑树结构实现的
分两种:
自然排序:
比较器排序
这种排序的使用取决于开发者是用什么样的构造方法
public class TreeSetDemo {
public static void main(String[] args){
// throw new IOException();
//创建TreeSet集合对象
//构造方法:
//public TreeSet():无参构造:根据其元素的自然顺序进行排序
//publict TreeSet(Comparaptr<E> com)
TreeSet<Integer> ts = new TreeSet<Integer>();//
//添加元素
//20,18,23,22,17,24,19,18,24
ts.add(20);// Integer i = Integer.valueOf(20) ;
ts.add(18) ;
ts.add(23) ;
ts.add(22) ;
ts.add(17) ;
ts.add(24) ;
ts.add(19) ;
ts.add(18) ;
ts.add(24) ;
//遍历这些元素
//增强for遍历
for(Integer i : ts){
System.out.print(i+ " ");//17 18 19 20 22 23 24 :唯一并且排序:自然排序(升序排序)
}
}
}
TreeSet集合存储自定义对象并遍历
对于TreeSet集合存储自定义对象必须实现一个接口:compareable接口
Student类中实现了compareable接口,重写了comapreTo()方法,里面的逻辑是一个排序条件:
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建TreeSet集合对象
TreeSet<Student> ts = new TreeSet<Student>() ;//
//创建学生对象
Student s1 = new Student("linqingxia", 28) ;
Student s2 = new Student("fengqingy", 28) ;
Student s3 = new Student("gaoyuanyuan", 27) ;
Student s4 = new Student("liushishi", 26) ;
Student s5 = new Student("wanglihong", 29) ;
Student s6 = new Student("zhangguorong", 30) ;
Student s7 = new Student("zhangguorong", 30) ;
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
//遍历
for(Student s : ts){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
需求:按照学生姓名长度从小到大进行排序
元素唯一性:取决返回值是否为0
要使用TreeSet集合的比较器排序:
依赖于构造方法:
public TreeSet(Comparator<E> comparator)
两种方式实现比较器排序:
Comparator接口作为形式参数进行传递,需要该接口的子实现类对象
方式1:自定义一个类,类实现Comparator接口,作为子实现类
方式2:可以使用接口的匿名内部类来实现(开发中,由于减少代码书写量,不需要自定义接口的子实现类,直接这种格式! )
public class TreeSetDemo {
public static void main(String[] args) {
//创建TreeSet集合使用比较器进行给元素进行排序
//public TreeSet(Comparator<E> comparator):有参构造
//使用接口的匿名内部类来实现
/**
* 格式
* new 接口名或者类名(){
* 重写方法() ;
* }
*/
// TreeSet<Student> ts = new TreeSet<Student>(new MyComparator()) ;
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// return 0;
int num = s1.getName().length() - s2.getName().length() ;
//次要条件:姓名长度一样,还要比较姓名的内容是否一样
int num2 = num==0 ? s1.getName().compareTo(s2.getName()): num ;
//姓名长度和内容都一样,还需比较两个人的年龄是否一样
int num3 = num2 ==0 ? s1.getAge() - s2.getAge() : num2 ;
return num3 ;
}
}) ;
//创建学生对象
Student s1 = new Student("gaoyuanyan", 27) ;
Student s2 = new Student("liushishi", 22);
Student s3 = new Student("fengqingy", 23) ;
Student s4 = new Student("wuqilong", 35) ;
Student s5 = new Student("gaoyuanyuan",27) ;
Student s6 = new Student("zhangguorong",29) ;
Student s7 = new Student("gaoyuanyuan",26) ;
//添加元素
ts.add(s1) ;
ts.add(s2) ;
ts.add(s3) ;
ts.add(s4) ;
ts.add(s5) ;
ts.add(s6) ;
ts.add(s7) ;
//增强for遍历
for(Student s : ts){
System.out.println(s.getName()+"----"+s.getAge());
}
}
}
键盘录入五个农药英雄比较他们的各自特点及总体性能
public class TreeSetTest {
public static void main(String[] args) {
//创建TreeSet集合
TreeSet<Hreo> ts = new TreeSet<Hreo>(new Comparator< Hreo>() {
@Override
public int compare(Hreo h1, Hreo h2) {
//条件
int num = h2.getSum() - h1.getSum();
int num2 = num == 0?h1.getLive()-h2.getLive():num;
int num3 = num == 0?h1.getAttack()-h2.getAttack():num2;
int num4 = num == 0?h1.getSkill()-h2.getSkill():num3;
int num5 = num == 0?h2.getDifficult()-h1.getDifficult():num4;
return num5;
}
});
System.out.println("录入英雄信息开始:");
//创建键盘录入并使用String类型接受
for(int x =1;x <= 5;x++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第"+x+"个英雄的姓名:");
String name = sc.nextLine();
System.out.println("请输入第"+x+"个英雄的生存值:");
String liveString = sc.nextLine();
System.out.println("请输入第"+x+"个英雄的技能值:");
String skillString = sc.nextLine();
System.out.println("请输入第"+x+"个英雄的难度值:");
String difficultString = sc.nextLine();
System.out.println("请输入第"+x+"个英雄的攻击值:");
String attackString = sc.nextLine();
//创建英雄对象
Hreo h = new Hreo();
//把英雄信息传进去
h.setName(name);
//将String类型转为int类型
h.setLive(Integer.parseInt(liveString));
h.setSkill(Integer.parseInt(skillString));
h.setDifficult(Integer.parseInt(difficultString));
h.setAttack(Integer.parseInt(attackString));
ts.add(h);
System.out.println("英雄信息录入完毕!");
System.out.println("英雄性能从高到低排列如下:");
System.out.println("姓名\t生存\t技能\t难度\t攻击");
//增强for遍历
for(Hreo h1:ts) {
System.out.println(h1.getName() + "\t" + h1.getLive() + "\t" + h1.getSkill() + "\t" +
h1.getDifficult() + "\t" + h1.getAttack());
}
}
}
}
获取10个1-20之间的随机数,要求不能重复
public class Test {
public static void main(String[] args) {
//1)创建一个随机数生成器
Random r = new Random();
//2)创建ArrayList集合,类型Integer
ArrayList<Integer> array = new ArrayList<Integer>() ;
//3)定义统计遍历
int count = 0 ;
//4)循环判断
while(count <10){
//通过随机数生成器获取:1-20之间的随机数public int nextInt(int n):生成随机数的范围:[0,n)
int number = r.nextInt(20) +1;
//有随机数了,还需要判断集合中是否包含这些随机数
if(!array.contains(number)){
//如果不包含,才添加到集合中
array.add(number) ;
count ++ ;
}
}
//遍历集合:增强for
for(Integer i : array){
System.out.println(i);
}
}
}