六、增强for循环、List、泛型、set集合

增强for循环

for(集合/数组中元素的数据类型 变量名 :  集合/数组名) {
	// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}

注意:

  • 实现Iterable接口的类才可以使用迭代器和增强for

  • 简化数组和Collection集合的遍历

基础练习

//增强for循环
ArrayList<String> ll = new ArrayList<String>();
ll.add("aaa");
ll.add("bbb");
ll.add("ccc");
ll.add("ddd");

//一个类的对象如果可以使用增强for循环,前提是类必须实现Iterable接口

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

如果希望获取当前数据的索引,就只能够自己定义变量,去统计了,如下:

int index = 0;
for (String s : ll) {
    index++;
    System.out.println(s);
}

遍历学生案例

用一个集合 存放学生对象,用增强for循环遍历打印所有学生信息,再用迭代器方式遍历

        //创建集合
        ArrayList<Student> list = new ArrayList<>();

        //添加学生
        list.add(new Student("zhangsan", 18));
        list.add(new Student("zhangsan1", 19));
        list.add(new Student("zhangsan2", 12));
        list.add(new Student("zhangsan3", 13));
        list.add(new Student("zhangsan4", 14));
        list.add(new Student("zhangsan5", 15));
        list.add(new Student("zhangsan6", 16));

   
        //增强for循环遍历学生
        for (Student stu : list) {
            System.out.println(stu.getName() + "-------" + stu.getAge());
        }

        //用迭代器遍历

        //获取迭代器
        Iterator<Student> iter = list.iterator();
        while (iter.hasNext()) {
            //获取当前的学生对象
            Student stu = iter.next();
            System.out.println(stu.getName() + "-------" + stu.getAge());
        }

普通for循环和增强for

  • 增强for 只是把集合中所有数据遍历出来

  • 普通for 因为控制条件是我们自己写的,可以控制循环的过程

List

  • 集合存取有序: 数据是按添加的顺序存储的,取的时候,可以使用索引按存储的顺序取出

  • 可以添加重复数据

特有方法介绍

方法名描述
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素

练习代码

List<String> list = new ArrayList();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
System.out.println(list);

//在索引2的位置添加数据xx
list.add(2, "xx");
System.out.println(list);

//删除索引为2的位置的数据  返回被删除的数据xx
String str01 = list.remove(2);
System.out.println(list);
System.out.println(str01);

//把索引为3的位置的数据 改为ff 返回被修改的dd
String str02 = list.set(3, "ff");
System.out.println(list);
System.out.println(str02);


//获取索引0位置的数据
String str03 = list.get(0);
System.out.println(str03);

  • 进栈(压栈) 出栈(弹栈)

  • 先进后出

队列:

  • 先进先出

数组 (数据的顺序存储方式)

  • 查询快 增删慢

链表 (数据的链式存储方式)

  • 增删快 查询慢

ArrayList源码(了解)

  • 数据是存到数组里的

LinkedList

  • 底层是链表结构 查询慢 增删快

特有方法

方法名说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素
//创建集合对象
List<String> list = new LinkedList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");

//头部(开始位置)添加数据
list.addFirst("mmm");
list.addFirst("nnn");
System.out.println(list);
//尾部(最后)添加数据
list.addLast("xxx");
list.addLast("yyy");
System.out.println(list);

//获取头部(第一个)数据
String first = list.getFirst();
//获取尾部(最后一个)数据
String last = list.getLast();
System.out.println(first);
System.out.println(last);

//删除头部(第一个)数据
String f = list.removeFirst();
System.out.println(f);
//删除尾部(最后一个)数据
String l = list.removeLast();
System.out.println(l);

System.out.println(list);

定义泛型(重点)

  • 常用字母 E T K V S U

    • T type表示类型,K,V代表键值,E代表element

类中:

格式: 修饰符 class 类名<代表泛型的变量> { }

案例

  • 定义一个学生类 定义一个属性 age,希望age的类型可以是Integer也可以是String

代码

  • 1 定义学生类 在类的后面添加泛型 <T> ,在定义age属性和相关getset方法时,就使用T

  • 2 测试 创建2个学生类对象, 分别传入不同的泛型Integer和String

可以定义多个泛型,逗号分开

类代码 两个泛型T 和E

  • T装饰age的类型 E装饰height的类型

//类中定义泛型 <T>
public class Student01<T, E> {

    //这里使用泛型T
    T age;
    E height;

    public T getAge() {
        return age;
    }

    public void setAge(T age) {
        this.age = age;
    }

    public E getHeight() {
        return height;
    }

    public void setHeight(E height) {
        this.height = height;
    }
}

测试代码

//创建学生对象  传入泛型 具体类型Integer和Double
Student01<Integer, Double> stu01 = new Student01<>();
stu01.setAge(10);
stu01.setHeight(10.5);


//创建学生对象  传入泛型 具体类型String
Student01<String, String> stu02 = new Student01<>();
stu02.setAge("20");
stu02.setHeight("50");

方法中(要配合参数使用,比较少使用)

格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }

语法练习

代码

  • 在show方法上定义泛型 ,在参数中 给age定义类型时使用了泛型

  • 一般配合参数使用,在调用的时候,传入什么类型, T就是什么类型

public class AA {
    //方法中定义的泛型
    //一般配合参数使用,在调用的时候,传入什么类型, T就是什么类型
    public <T> void show(T age) {
        T a = age;
        System.out.println(a);
    }
}

测试

public class AA {
    //方法中定义的泛型
    //一般配合参数使用,在调用的时候,传入什么类型, T就是什么类型
    public <T> void show(T age) {
        T a = age;
        System.out.println(a);
    }
}

接口中(重点)

格式:修饰符 interface 接口名<代表泛型的变量> { }

接口和实现类代码

  • 定义了一个接口 里面定义了泛型

  • 两个实现类

//定义接口中的泛型T 在方法中使用了这个T定义参数e
interface inter1<T> {
    public void show(t e, int a);

}
//1 写一个类继承接口inter1的时候 就把泛型确定好,比如这里是String,那么show方法中的参1也就是String
class A implements inter1<Integer> {
    @Override
    public void show(t e, int a) {
        System.out.println(e + "----" + a);
    }
}
//2  B后面的E是给类定义了一个泛型
// inter1后面的E是使用了类的泛型,把它再传给接口inter1里的泛型T
// show方法是对接口的抽象方法的实现,参数里的E 和Inter1<E>这里的E保持一致
// method参数里的E只是使用了类的泛型E
class B<E> implements inter1<E> {
    @Override
    public void show(t e, int a) {
        System.out.println(e + "----" + a);
    }
    public void method(E height) {
        System.out.println(height);
    }
}

测试方法

    public static void main(String[] args) {
        A a = new A();
        a.show(10, 20);
    
        B<String> b = new B<>();
        b.show("aaa", 100);
    }

类型通配符 ?

类型通配符: <?>

  • ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型

  • 但是ArrayList就不能添加数据了,获取出来的也是Object类型

  • 使用通配符的集合不能够修改数据了

代码

  • showList方法,接收一个集合类型的数据,可以打印结合中所有数据,但不知道集合里具体类型,索引泛型写了?

  • main方法中 创建了两个不同类型的集合对象,传入测试

    public static void main(String[] args) {
        ArrayList<String> list01 = new ArrayList<>();
        list01.add("aa");
        list01.add("bb");
        list01.add("cc");

        showList(list01);

        ArrayList<Boolean> list02 = new ArrayList<>();
        list02.add(true);
        list02.add(false);
        list02.add(false);

        showList(list02);

    }


    public static void showList(ArrayList<?> list) {
        for (Object o : list) {
            System.out.println(o);
        }
    }

类型通配符上限: <? extends 类型> (源码中常见)

  • ArrayList<? extends Number>: 它表示的类型是Number或者其子类型

类型通配符下限: <? super 类型> (用的很少)

  • ArrayList <? super Number>: 它表示的类型是Number或者其父类型

代码

    public static void main(String[] args) {
        ArrayList<Integer> list01 = new ArrayList<>();
        list01.add(11);
        list01.add(22);
        showList(list01);

        ArrayList<Double> list02 = new ArrayList<>();
        list02.add(1.5);
        list02.add(5.1);

        showList(list02);
    }

    //<? extends Number>表示只能传入 Number类型的对象 或者是子类的对象
    public static void showList(ArrayList<? extends Number> list) {
        for (Number number : list) {

        }
    }

    //传入的必须是Number类型的对象或者是Number的父类的对象  用的极少极少极少极少极少
    public static void showList1(ArrayList<? super Number> list) {
        for (Object o : list) {

        }
    }

set集合

TreeSet HashSet

特点

  • 1 不能存储重复数据

  • 2 没有索引,不能使用普通的for循环。 增强for和迭代器都可以

  • 3 存取顺序不一致

基本代码练习

        //创建集合对象
        Set<String> set = new TreeSet<>();
        //添加数据  注意这里重复添加了ddd和bb  第2个ddd和bb不会添加成功
        set.add("ccc");
        set.add("ddd");
        set.add("bb");
        set.add("bb");
        set.add("ddd");
        set.add("ee");
        set.add("aaa");

//        for (int i = 0; i < set.size(); i++) {
//            //Set集合是没有索引的,所以不能使用通过索引获取元素的方法
//        }

        //迭代器的方式 遍历
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-----------------------------------");
        //增强for遍历
        for (String s : set) {
            System.out.println(s);
        }

treeset (自然排序是重点掌握)

  • 会给数据排序

TreeSet<Integer> set = new TreeSet<>();

set.add(50);
set.add(30);
set.add(70);
set.add(60);
set.add(20);
set.add(100);

System.out.println(set);

添加学生对象 报错

  • 原因是学生类 没有实现Comparable接口

解决错误 (自然排序)

给学生实现Comparable接口

  • 因为后面要比较的是学生对象,索引接口那里添加具体的泛型的类型为Student

  • 这里重写的compareTo方法里,是按年龄排序,后续再解释原理

再次运行刚才添加学生的报错的代码

  • 发现可以按照年龄排序了

学生按年龄排序图

 

需求 先按你年龄排序 如果年龄相等 按姓名排序

  • 修改Student类里 的compareTo方法

    • 1先比较年龄 得到一个值result

    • 2 result如果等于0 说明年龄相等 再比较姓名

    @Override
    public int compareTo(Student o) {
        int result = this.age - o.age;

        //按照年龄从小到大排序 如果年龄相等 按照名字排序
        return result == 0 ? this.name.compareTo(o.name) : result;

        //return this.age - o.age;
        //return o.age - this.age;
    }

测试

TreeSet<Student> ts = new TreeSet<>();
ts.add(new Student("zs", 24));
ts.add(new Student("lisi", 19));
ts.add(new Student("xiaom", 30));
ts.add(new Student("daming", 50));
ts.add(new Student("wangwu", 25));
ts.add(new Student("hehe", 30));
ts.add(new Student("daming", 50));

System.out.println(ts);

Comparator比较器方式排序

为什么用比较器?

  • 1我有一个需求,两个集合,各存几个学生,一个集合希望按照年龄从小到大,另外一个希望从大到小

    • 用自然排序,只能再学生类中的compareTo中写一种规则,索引需要用比较器

    • 比较器可以在创建集合的时候,传入到集合中,一个比较器对象可以制定一种规则。

  • 2 有一个需求,希望集合中的字符串按照从大到小排序,那么使用自然排序,得修改String的代码

    • 只能用比较器排序

比较器排序Comparator的使用(重点掌握)

  • 案例需求

    • 两个集合,各存3个学生

    • 一个集合按照年龄从小到大排序,另外一个按照年龄从大到小排序

  • 实现步骤

    • 创建TreeSet集合对象时利用构造方法传入比较器对象。

    • 比较器对象以匿名内部类或者lambda表达式的形式传入,重写compare(T o1,T o2)方法,集合就会按照compare方法的规则排序

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

Student类代码

package com.itheima.test1;

public  class Student implements Comparable<Student>{
    String name;
    int age;

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

    public Student() {
    }

    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 "{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
      

      @Override
      public int compareTo(Student o) {
          return 0;
      }
}

测试类代码

  • 创建了两个集合set1和set2,分别用匿名内部类和lambda的方式传入的比较器对象

  • 注意set2中的lambda方式,由于存在泛型,所以添加 (Comparator<Student>)

   public static void main(String[] args) {
        //按照年龄从小到大排
        TreeSet<Student> ts01 = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.age - o2.age;
            }
        });
        ts01.add(new Student("zs", 24));
        ts01.add(new Student("xiaom", 30));
        ts01.add(new Student("lisi", 19));
        ts01.add(new Student("daming", 50));
        ts01.add(new Student("wangwu", 25));
        ts01.add(new Student("hehe", 30));
        ts01.add(new Student("daming", 50));
        System.out.println(ts01);

        //按照年龄从大到小排
        TreeSet<Student> ts02 = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.age - o1.age;
            }
        });
        ts02.add(new Student("zs", 24));
        ts02.add(new Student("xiaom", 30));
        ts02.add(new Student("lisi", 19));
        ts02.add(new Student("daming", 50));
        ts02.add(new Student("wangwu", 25));
        ts02.add(new Student("hehe", 30));
        ts02.add(new Student("daming", 50));
        System.out.println(ts02);
    }
  • lambda实现

//按照年龄从小到大排
TreeSet<Student> ts01 = new TreeSet<>((o1, o2) -> o1.age - o2.age);

ts01.add(new Student("zs", 24));
ts01.add(new Student("xiaom", 30));
ts01.add(new Student("lisi", 19));
ts01.add(new Student("daming", 50));
ts01.add(new Student("wangwu", 25));
ts01.add(new Student("hehe", 30));
ts01.add(new Student("daming", 50));
System.out.println(ts01);

//按照年龄从大到小排
TreeSet<Student> ts02 = new TreeSet<>((o1, o2) -> o2.age - o1.age);

ts02.add(new Student("zs", 24));
ts02.add(new Student("xiaom", 30));
ts02.add(new Student("lisi", 19));
ts02.add(new Student("daming", 50));
ts02.add(new Student("wangwu", 25));
ts02.add(new Student("hehe", 30));
ts02.add(new Student("daming", 50));
System.out.println(ts02);

两种比较方式总结

  • 两种比较方式小结

    • 自然排序: 需要比较的对象的类要实现Comparable接口,重写compareTo方法,根据返回值进行排序

    • 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序

    • 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序

  • 两种方式中关于返回值的规则

    • 如果返回值为负数,表示当前存入的元素是较小值,存左边

    • 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存

    • 如果返回值为正数,表示当前存入的元素是较大值,存右边

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值