集合-2

1. 常用Set集合

1.1 Set集合的特点

  1. Set接口下的集合都会有以下特点
  • 不能存储重复元素
  • 没有索引

1.2 HashSet

HashSet集合的特点

  • 底层数据结构是哈希表
  • 存储元素的顺序和遍历获取出来的顺序可能不一致
  • 没有索引
  • 集合中不能存储重复元素

1.2.1 创建对象

  1. HashSet<元素数据类型> set = new HashSet<>();
  1. public static void main(String[] args) {
  2. HashSet<String> set = new HashSet<>();
  3. }

1.2.2 常用方法

  1. boolean add(E e) //添加元素,如果元素添加不成功 返回值代表是否添加成功,
  2. boolean remove(Object o) //删除元素 ,返回值代表删除元素是否成功
  3. boolean contains(Object o) //判断元素是否存在
  4. int size() //获取集合的大小
  1. public static void main(String[] args) {
  2. HashSet<String> set = new HashSet<>();
  3. //添加元素
  4. boolean f = set.add("三");
  5. set.add("更");
  6. set.add("草");
  7. set.add("堂");
  8. boolean f2 = set.add("三");
  9. //删除元素
  10. boolean f3 = set.remove("三");
  11. boolean f4 = set.remove("三");
  12. //判断元素是否存在
  13. boolean f5 = set.contains("更");
  14. //获取集合的大小
  15. int size = set.size();
  16. }

1.2.3 遍历

1.转换为数组遍历

  1. public static void main(String[] args) {
  2. HashSet<String> set = new HashSet<>();
  3. set.add("三");
  4. set.add("更");
  5. set.add("草");
  6. set.add("堂");
  7. String[] strings = set.toArray(new String[0]);
  8. for (int i = 0; i < strings.length; i++) {
  9. System.out.println(strings[i]);
  10. }
  11. }

2.使用迭代器遍历

  1. public static void main(String[] args) {
  2. HashSet<String> set = new HashSet<>();
  3. set.add("三");
  4. set.add("更");
  5. set.add("草");
  6. set.add("堂");
  7. Iterator<String> it = set.iterator();
  8. while (it.hasNext()){
  9. String s = it.next();
  10. System.out.println(s);
  11. }
  12. }

3.foreach遍历

  1. public static void main(String[] args) {
  2. HashSet<String> set = new HashSet<>();
  3. set.add("三");
  4. set.add("更");
  5. set.add("草");
  6. set.add("堂");
  7. for (String s : set) {
  8. System.out.println(s);
  9. }
  10. }

2. 泛型

2.1 概述

  1. 泛型可以把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型
  2. 相当于把数据类型作为参数来进行传递。
  3. **注意:泛型只能是引用数据类型。**

2.2 使用

2.2.1 泛型类&泛型接口

  1. 泛型类和泛型接口的用都相同,下面我们以泛型类为例进行讲解。
  2. 泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来

2.2.1.1 定义泛型
  1. 在类名后加<>,在<>中定义泛型,<>中的内容相当于泛型的名字,可以随便写。在泛型类中我们可以把这个泛型的名字当做一个数据类型来使用。
  1. public class TestClass<T> {
  2. //...
  3. }

2.2.1.2 使用泛型
  1. 在泛型类中可以使用在类名后面定义的泛型。
  1. public class TestClass<T> {
  2. public void test(T t){
  3. }
  4. }

2.2.1.3 泛型的确定

①创建对象时确定

  1. 在创建泛型类对象的时候确定之前定义的泛型代表什么数据类型。在定义泛型类对象的时候,在类名的后加<>,在其中写一个具体的数据类型。
  1. public static void main(String[] args) {
  2. TestClass<String> t = new TestClass();//指定了该对象的泛型T是String类型
  3. t.test("三更草堂");//所以test方法的参数类型应该也是String类型
  4. }

②定义子类时确定

  1. 在定义子类的时候可以确定泛型。具体用法如下:
  1. public class SubClass extends TestClass<String> {
  2. @Override
  3. public void test(String s) {
  4. }
  5. }
  1. 这样在子类SubClass中泛型就确定为String类型了。

注意:我们在定义子类时也可以选择不确定泛型,让其在创建对象的时候在确定。写法如下

  1. public class SubClass<T> extends TestClass<T> {
  2. @Override
  3. public void test(T t) {
  4. super.test(t);
  5. }
  6. }

2.2.2泛型方法

2.2.2.1 定义泛型
  1. 在方法返回值类型的前面加<>,在<>中定义泛型,<>中的内容相当于泛型的名字,可以随便写。在该泛型方法中我们可以把这个泛型的名字当做一个数据类型来使用。
  1. public static <T> T test(T t){
  2. return t;
  3. }

2.2.2.2 使用泛型
  1. 在泛型方法中可以使用定义的泛型。并且我们一般是在参数列表中或者是返回值类型上使用到这个泛型。
  1. public static <T> T test(T t){
  2. return t;
  3. }

2.2.2.3 泛型的确定
  1. 在**调用泛型方法**的时候才真正确定之前定义的泛型代表什么数据类型。在**调用泛型方法**的时候,程序会根据你的调用自动推导泛型的具体类型。
  1. public static void main(String[] args) {
  2. Integer test = test(1);
  3. String s = test("三更草堂");
  4. }

2.3 泛型上限&泛型下限

2.3.1 泛型限定的概念

  1. 我们在使用确定泛型的时候可以使用任意的引用数据类型去确定。但是在某些场景下我们要求这个泛型必须是某个类的子类或者是某个类的父类。这种情况下我们就需要用到泛型上限和泛型上限来限制泛型的范围。

2.3.1 泛型上限

  1. 限制泛型必须是某个类或者是其子类。

格式:

  1. <? extends 具体的类型>

例如:

  1. public static void test(List<? extends Person> t){
  2. }

这样我们再调用test方法的时候只能存入泛型为Person或者是Person子类的List集合对象。

2.3.2 泛型下限

  1. 限制泛型必须是某个类或者是其父类。

格式:

  1. <? super 具体的类型>

例如:

  1. public static void test(List<? super Student> t){
  2. }

这样我们再调用test方法的时候只能存入泛型为Student或者是Student父类的List集合对象。

2.3.3 注意事项

  1. 1.泛型上限可以在定义泛型类和方法参数上使用
  1. public class Box<E extends Person> {
  2. E e;
  3. }
  1. 2.泛型下限主要在方法参数上使用。