Java-进阶-day06-集合进阶_02

Java-进阶-day06-集合进阶_02

今日内容:

  • Set集合
  • 必须掌握: Set集合的基本使用方式以及又什么特点
  • 重点理解: Set集合存储不重复元素的唯一的原理
    *必须掌握 Set集合的遍历方式
  • 泛型
  • 可变参数

一.Set集合

Set集合概述和特点

总结:

  • Set集合的特点

    • 不允许存储重复的元素
    • 没有索引
  • Set集合的基本使用
    new HashSet<>();

哈希值【理解】

总结:

  • 哈希值简介
    * 用于计算存储元素的位置的. 提高存储元素和取出元素的效率的

  • 如何获取哈希值
    * hashCode()

  • 哈希值的特点

    • 同一个对象的哈希值是一样的
    • 默认情况下,不同的对象的哈希值是不一样的,重写hashCode就能达到不同的对象的哈希值是一样的

HashSet集合概述和特点

总结:

  • HashSet集合的特点

    • 无序(存储的顺序和取出的顺序有可能是不一致的)
    • 不允许存储重复的元素
    • 没有索引

数据结构:


JDK8之前: 哈希表(数组 + 链表)


JDK8之后: 哈希表(数组 + 链表 + 红黑树)

  • HashSet集合的基本使用
	HashSet<String> set = new HashSet<>();
    set.add("abc");
    set.add("def");
    set.add("egi");

    for (String s : set) {
        System.out.println(s);
    }

HashSet集合保证元素唯一性源码分析

  • HashSet集合保证元素唯一性的原理
    • 首先计算该元素的哈希值(hash()),在哈希表中找有没有相同的哈希值
      • 如果没有相同的哈希值,认定为这个元素不是一个重复的元素,添加到哈希表中
      • 如果有相同的哈希值,必须再调用equals方法判断相同的哈希值的内容是否是一样的
        • 如果内容一样: 认定为这个元素就是一个重复的元素,不存储
        • 如果内容不一样:认定为这个元素不是一个重复的元素,存储到哈希表中
    • 结论:
      要想判断一个元素是否是唯一的,必须重写hashCode和equals方法

常见数据结构之哈希表【理解】

理解即可

LinkedHashSet集合概述和特点【应用】

  • LinkedHashSet集合特点

    • 有序(存储的顺序和取出的顺序是一致的)
    • 不允许存储重复的元素
    • 没有索引

    数据结构:
    数组+链表 + 链表 ==> 数组 + 双向链表

  • LinkedHashSet 集合基本使用

public class LinkedHashSetDemo01 {
    public static void main(String[] args) {
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add(new String("重地"));
        linkedHashSet.add(new String("通话"));
        linkedHashSet.add(new String("cde"));
        System.out.println(linkedHashSet);
    }
}

TreeSet集合概述和特点

总结:

  • TreeSet集合的特点:
    • 有序(按照一定的规则进行排序,形成一定顺序的数据)
    • 不允许存储重复的元素
    • 没有索引

数据结构:
二叉树 --> 红黑树

  • TreeSet构造方法
    • TreeSet(): 按照自然顺序进行排序
    • TreeSet(Comparator comparator) : 按照比较器进行排序

自然排序Comparable的使用

  • 案例需求

存储学生对象并遍历,创建TreeSet集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

  • 实现步骤

用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

*案例代码:*
public class TreeSetDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();

        //创建学生对象
        Student s1 = new Student("xishi", 29);
        Student s2 = new Student("wangzhaojun", 28);
        Student s3 = new Student("diaochan", 30);
        Student s4 = new Student("yangyuhuan", 33);

        Student s5 = new Student("linqingxia",33);
        Student s6 = new Student("linqingxia",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }

}


public class Student implements Comparable<Student> {
	private String name;
	private int age;
	@Override
	public int compareTo(Student s) {
	//        return 0;  表示比较的两个对象相等
	//        return 1;   表示的是按照升序排序
	//        return -1;  表示的是按照降序排序
	    //按照年龄从小到大排序
	    // int num = this.age - s.age;
	    //  按照年龄从大到小排序
	    int num = s.age - this.age; // 主要条件
	    // s.name.length() - this.name.length();
	    // s.name.charAt(0) - this.name.charAt(0);
	    //年龄相同时,按照姓名的字母顺序排序
	
	    // 次要条件
	    int num2 = num == 0 ? this.name.compareTo(s.name) : num;
	
	    return num2;
	
	
	}
}
		

比较器排序Comparator的使用

  • 案例需求

存储学生对象并遍历,创建TreeSet集合使用带参构造方法

要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

  • 实现步骤

用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法

重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写

案例代码:

public class TreeSetDemo {
    public static void main(String[] args) {
        //创建集合对象
        // 先定义一个Comparator实现类 重写compare方法,在创建TreeSet的时候,将omparator实现类的对象给这个构造
        TreeSet<Student> ts = new TreeSet<Student>(new ComparatorImpl());

        //创建学生对象
        Student s1 = new Student("xishi", 29);
        Student s2 = new Student("wangzhaojun", 28);
        Student s3 = new Student("diaochan", 30);
        Student s4 = new Student("yangyuhuan", 33);

        Student s5 = new Student("linqingxia",33);
        Student s6 = new Student("linqingxia",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

class ComparatorImpl implements Comparator<Student>{


    public int compare(Student s1, Student s2) {
        //this.age - s.age
        //s1,s2  s1 - s2 按照升序进行排序  s2 - s1 : 按照降序进行排序
        int num = s1.getAge() - s2.getAge();
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
        return num2;
    }
}

public class Student {
    private String name;
    private int age;
}

成绩排序案例【应用】

  • 案例需求
    • 用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
    • 要求:按照总分从高到低出现
public class TreeSetDemo {
    public static void main(String[] args) {
        //创建TreeSet集合对象,通过比较器排序进行排序
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
//                int num = (s2.getChinese()+s2.getMath())-(s1.getChinese()+s1.getMath());
                //主要条件 按照总成绩的降序排序
                int num = s2.getSum() - s1.getSum();
                //次要条件 如果总成绩相等 就按照语文成绩进行排序 按照语文的升序进行排序
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                // 如果是总成绩一样 语文成绩也一样就按照名字进行排序 ,按照自然顺序进行排序
                int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
                return num3;
            }
        });

        //创建学生对象
        Student s1 = new Student("林青霞", 98, 100);
        Student s2 = new Student("张曼玉", 95, 95);
        Student s3 = new Student("王祖贤", 100, 93);
        Student s4 = new Student("柳岩", 100, 97);
        Student s5 = new Student("风清扬", 98, 98);

        Student s6 = new Student("左冷禅", 97, 99);
//        Student s7 = new Student("左冷禅", 97, 99);
        Student s7 = new Student("赵云", 97, 99);

        //把学生对象添加到集合
        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.getChinese() + "," + s.getMath() + "," + s.getSum());
        }
    }
}


public class Student {
    private String name;
    private int chinese;
    private int math;

    public Student() {
    }

    public Student(String name, int chinese, int math) {
        this.name = name;
        this.chinese = chinese;
        this.math = math;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getMath() {
        return math;
    }

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

    public int getSum() {
        return this.chinese + this.math;
    }
}

不重复的随机数案例【应用】

  • 案例需求

编写一个程序,获取10个1*20之间的随机数,要求随机数不能重复,并在控制台输出

*案例代码: *

Set<Integer> set = new TreeSet<Integer>();
//创建随机数对象
Random r = new Random();

//判断集合的长度是不是小于10
while (set.size()<7) {
    //产生一个随机数,添加到集合
    // int number = r.nextInt(20) + 1;
    set.add(r.nextInt(35) + 1);
}

//遍历集合
for(Integer i : set) {
    System.out.println(i);
}

二.泛型

泛型概述和好处

总结:

  • 泛型概述
    广泛的类型 只有在使用的时候才能确定是一个什么样的类型

  • 泛型定义格式
    <类型>
    <类型1,类型2>

  • 泛型的好处

    • 将运行时期的异常提前到了编译时期

    • 避免了强制类型转换(避免出现 ClassCastException)

    • 有泛型没有写,泛型的类型默认都是Object

    • 泛型在编译时期是有泛型 运行时期是没有泛型的 泛型擦除

    如果一个类后面有<>
    第一: 可以不写泛型 泛型的类型默认都是Object
    第二: 写泛型
    如果一个类后面没有<>,绝对不允许写泛型

泛型类

总结:

  • 定义格式

public class 类名<类型>{

}

 T  :  Type
 E  :  Element
 K  :  Key
 V  :  Value


public class StudentDemo01 {
    public static void main(String[] args) {
        Student<PC> objectStudent = new Student<>();
        objectStudent.setType(new PC());
    }
}

 public class Student<T> {

	    private T type;

	    public T getType() {
	        return type;
	    }

	    public void setType(T type) {
	        this.type = type;
	    }
 }

泛型方法

总结:

  • 定义格式
 // 定义泛型方法
public <T> void 方法名(T t){

}

泛型接口

总结:

  • 定义格式
public interface 接口名<类型>{

}

实现泛型接口的格式:
public class InterImpl<T> implements Inter<T> {
    @Override
    public void method(T t) {
    }
}

public class InterImplA implements Inter<String> {
    @Override
    public void method(String s) {

    }
}

类型通配符【应用】

总结:

类型通配符的分类

  • 类型通配符:

    public static void printArrayList(ArrayList list) {}
    
    • 类型通配符上限:

      List:它表示的类型是Number或者其子类型

    • 类型通配符下限:

      List:它表示的类型是Number或者其父类型

三.可变参数

可变参数

总结:

  • 什么是可变参数
    参数的个数是可变的, 数据类型已经确定了,但是个数不能确定,这个时候就可以使用可变参数

  • 可变参数定义格式
    格式:
    数据类型…变量名

  • 可变参数的注意事项

    • 可变参数的本质是数组
    • 可变参数在进行传递的时候,可以给0个 可以给多个
    • 如果方法内有多个参数的时候,包含可变参数,这个可变参数一定要放在最后

可变参数的使用【应用】

总结:

  • Arrays 工具类中有一个静态方法:
	// 将数组转换成List集合
  * public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表

  • 返回的集合不能做增删操作,可以做修改操作
	// 将集合转换成数组
	Object[] toArray()  
	<T> T[]  toArray(T[] a)  
  • List接口中有一个静态方法:

    • public static List of(E… elements):返回包含任意数量元素的不可变列表
    • 返回的集合不能做增删改操作
  • Set接口中有一个静态方法:

    • public static Set of(E… elements) :返回一个包含任意数量元素的不可变集合
    • 在给元素的时候,不能给重复的元素
    • 返回的集合不能做增删操作,没有修改的方法

课后练习

练习一

  • 需求:

LinkedHashSet基本使用

使用LinkedHashSet存储以下元素:
“王昭君”,“王昭君”,“西施”,“杨玉环”,“貂蝉”。
使用迭代器和增强for循环遍历LinkedHashSet。


public class Demo_1 {

	public static void main(String[] args) {

		LinkedHashSet<String> lhs = new LinkedHashSet<>();
		lhs.add("王昭君");
		lhs.add("王昭君");
		lhs.add("西施");
		lhs.add("杨玉环");
		lhs.add("貂蝉");

		Iterator<String> iterator = lhs.iterator();

		while (iterator.hasNext()) {
			String s = iterator.next();
			System.out.println(s);
		}
		System.out.println("~~~~~~~~~~~~");

		for (String lh : lhs) {
			System.out.println(lh);
		}

	}
}


练习二

  • 需求:

根据需求完成代码

1.定义一个Person类
成员变量:name age
构造方法、get和set、toString

2.创建三个Person对象
Person(“张三”,23);
Person(“李四”,24);
Person(“张三”,23);

3.将这三个对象存储到Set集合中。(同姓名同年龄的为重复值、不存储)

4.使用迭代器遍历集合

实现了Comparable接口的Person类

public class Person implements Comparable<Person> {

	private String name;
	private int age;

	public Person() {
	}

	public Person(String name, int age) {
		this.name = name;
		this.age = 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;
	}

	@Override
	public String toString() {
		return "Person{" +
				"name='" + name + '\'' +
				", age=" + age +
				'}';
	}

	@Override
	public int compareTo(Person person) {
		int nameDiff = this.name.compareTo(person.name);
		return nameDiff == 0 ? this.age - person.age : nameDiff;
	}
}


测试类


public class PersonDemo {


	public static void main(String[] args) {

		Person p1 = new Person("张三", 23);
		Person p2 = new Person("李四", 24);
		Person p3 = new Person("张三", 23);

		Set<Person> set = new TreeSet<>();
		set.add(p1);
		set.add(p2);
		set.add(p3);

		Iterator<Person> it = set.iterator();

		while (it.hasNext()) {
			Person p = it.next();
			System.out.println(p);
		}

	}
}

练习三

  • 需求:

根据需求完成代码

1.键盘录入一个字符串,去掉其中重复字符

2.打印出不同的那些字符,必须保证顺序。

例如输入:aaaabbbcccddd,打印结果为:abcd。

public class SetDemo_03 {

	public static void main(String[] args) {

		Set<Character> set = new TreeSet<>();	//字符的封装类
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入字符串:");
		String str = sc.next();
		char[] arr = str.toCharArray();	//将字符串转换为字符数组

		for (char c : arr) {
			set.add(c);
		}

		System.out.println(set);
	}
}


练习四

  • 需求:

已知数组存放一批QQ号码,QQ号码最长为11位,最短为5位

String[] str{“12345”,“67891”,“12347809933”,“98765432102”,“67891”,“12347809933”};

将该数组里面的所有qq号都存放在LinkedList中,将list中重复元素删除,
将list中所有元素分别用迭代器和增强for循环打印出来。


public class Demo_05 {
dddd
	public static void main(String[] args) {

		String[] str = {"12345","67891",
				"12347809933","98765432102","67891","12347809933"};
		Set<String> set = new HashSet<>();

		Collections.addAll(set, str);
		LinkedList<String> list = new LinkedList<>(set);


		Iterator<String> it = list.iterator();

		while (it.hasNext()) {
			String s = it.next();
			System.out.println(s);
		}

		System.out.println("~~~~~~~~");

		for (String s : list) {
			System.out.println(s);
		}

	}
}

``

#### 练习四

* 需求:

> 已知数组存放一批QQ号码,QQ号码最长为11位,最短为5<br>
String[] str{"12345","67891","12347809933","98765432102","67891","12347809933"};<br>
将该数组里面的所有qq号都存放在LinkedList中,将list中重复元素删除,
将list中所有元素分别用迭代器和增强for循环打印出来。

```java

public class Demo_05 {
dddd
	public static void main(String[] args) {

		String[] str = {"12345","67891",
				"12347809933","98765432102","67891","12347809933"};
		Set<String> set = new HashSet<>();

		Collections.addAll(set, str);
		LinkedList<String> list = new LinkedList<>(set);


		Iterator<String> it = list.iterator();

		while (it.hasNext()) {
			String s = it.next();
			System.out.println(s);
		}

		System.out.println("~~~~~~~~");

		for (String s : list) {
			System.out.println(s);
		}

	}
}

你可能感兴趣的:(Java进阶)