泛型、迭代、foreach问题

泛型

  • 泛型是⼀种强类型约束机制,可以在编译期间检查类型安全性,并且可以提⾼代码的复⽤性和可读性
  • 类型参数化
    泛型的本质是参数化类型,也就是说,在定义类、接⼝或⽅法时,可以使⽤⼀个或多个类型参数来表示参数化类型
public class Box<T> {
private T value;
public Box(T value) {
this.value = value;
 }
public T getValue() {
return value;
 }
public void setValue(T value) {
this.value = value;
 }
}
----------------------------------------
Box<Integer> intBox = new Box<>(123);
Box<String> strBox = new Box<>("Hello, world!");
  • 类型擦除
    泛型在编译时会将泛型类型擦除,将泛型类型替换成 Object 类型。这是为了向后兼容,避免对原有的 Java 代码造成影响。
List<Integer> intList = new ArrayList<>();
intList.add(123);
int value = intList.get(0);
-----------------------------------------------------
List intList = new ArrayList();
intList.add(Integer.valueOf(123));
int value = (Integer) intList.get(0);

在编译时,Java 编译器会将泛型类型 List 替换成 List ,将 get ⽅法的返回值类型 Integer 替换成 Object,Java 泛型只在编译时起作⽤,运⾏时并不会保留泛型类型信息

  • 通配符
    ⽤于表示某种未知的类型,例如 List<?> 表示⼀个可以存储任何类型对象的 List,但是不能对其中的元素进⾏添加操作。通配符可以⽤来解决类型不确定的情况,例如在⽅法参数或返回值中使⽤。
    使⽤通配符可以使⽅法更加通⽤,同时保证类型安全。
public static void printList(List<?> list) {
       for (Object obj : list) {
             System.out.print(obj + " ");
     }
     System.out.println();
}
  • 上限通配符
    泛型还提供了上限通配符 <? extends T> ,表示通配符只能接受 T 或 T 的⼦类。使⽤上限通配符可以提⾼程序的类型安全性。
public static void printNumberList(List<? extends Number> list) {
        for (Number num : list) {
              System.out.print(num + " ");
        }
      System.out.println();
}
  • 下限通配符
    下限通配符(Lower Bounded Wildcards)⽤ super 关键字来声明,其语法形式为 <? super T> ,其中 T 表示类型参数。它表示的是该类型参数必须是某个指定类的超类(包括该类本身)。
    当我们需要往⼀个泛型集合中添加元素时,如果使⽤的是上限通配符,集合中的元素类型可能会被限制,从⽽⽆法添加某些类型的元素。但是,如果我们使⽤下限通配符,可以将指定类型的⼦类型添加到集合中,保证了元素的完整性。
    需要注意,虽然使⽤下限通配符可以添加某些⼦类型元素,但是在读取元素时,我们只能确保其是 Object 类型的,⽆法确保其是指定类型或其⽗类型。因此,在读取元素时需要进⾏类型转换
  • PECS原则(Producer Extends, Consumer Super)

iterator和iterable

  • list三种遍历方式for循环、迭代器、for-each(语法糖,默认通过intertor()方法遍历)
  • JDK 1.8 时,Iterable 接⼝中新增了 forEach ⽅法。该⽅法接受⼀个 Consumer 对象作为参数,⽤于对集合中的每个元素执⾏指定的操作。该⽅法的实现⽅式是使⽤ for-each 循环遍历集合中的元素,对于每个元素,调⽤ Consumer 对象的 accept ⽅法执⾏指定的操作。
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
list.forEach(integer -> System.out.println(integer));
----------------------------------------------------------------------
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
list.forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer integer) {
             System.out.println(integer);
 }
});
  • itrator接口 extends itrable接口,itrable接口含有iterator().
  • itrator实现迭代功能(hasnext、next、remove),iterable支持迭代.
  • 不直接把迭代功能放到interable中,集合可以实现不同的遍历方式实现了Iterable的类可以再实现多个Iterator内部类,例如LinkedList中的ListItr和DescendingIterator两个内部类,就分别实现了双向遍历和逆序遍历。通过返回不同的Iterator实现不同的遍历方式,这样更加灵活。如果把两个接口合并,就没法返回不同的Iterator实现类。

for-Each问题

为什么不能在foreach⾥执⾏删除操作?
因为 foreach 循环是基于迭代器实现的,⽽迭代器在遍历集合时会维护⼀个 expectedModCount属性来记录集合被修改的次数。如果在 foreach 循环中执⾏删除操作会导致 expectedModCount属性值与实际的 modCount 属性值不⼀致,从⽽导致迭代器的 hasNext() 和 next() ⽅法抛出ConcurrentModificationException 异常。
为了避免这种情况,应该使⽤迭代器的 remove() ⽅法来删除元素,该⽅法会在删除元素后更新迭代器状态,确保循环的正确性。如果需要在循环中删除元素,应该使⽤迭代器的 remove() ⽅法,⽽不是集合⾃身的 remove() ⽅法。
for循环没问题是调用迭代器,没有checkmod。

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
 String str = itr.next();
 if ("1".equals(str)) {
 itr.remove();
 }
}
---------------------------------------------------------------------------------
List<String> list = new ArrayList<>(Arrays.asList("1","2","3"));
list.stream().filter(i - !i.equals("1")).collect(Collectors.toList());
  • fail-fast机制
    优先考虑错误,检测到错误马上抛出,方便上级程序做相应处理。避免 程序继续执行引起更大的问题。
  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值