今天在看尚硅谷的课程时里面讲了这么一句话:
集合在遍历时需要先创建一个容器,存放集合的数据,这样做浪费内存
想去验证下,就翻了翻ArrayList
的迭代过程源码
在ArrayList
的迭代器类Itr
(ArrayList
的内部类)源码中有这么一段代码
Object[] elementData = ArrayList.this.elementData;
结论:
这行代码是Java中内部类(Inner Class)中的一种用法,通过类名.this
来引用外部类的实例。
在上述代码中,
ArrayList.this
表示在ArrayList
的非静态内部类Itr
中,引用外部的ArrayList实例的elementData
属性。
在Java中,内部类有时候会引用外部类的实例。为了明确地表示使用的是外部类的实例而不是内部类的实例,可以使用外部类名.this
的形式。
在 Java 中,内部类(Inner Class)可以访问其外部类(Enclosing Class)的实例,这是因为内部类被认为是外部类的一部分。这种访问的机制涉及到内部类的实现方式以及编译器的处理。
有两种主要的内部类:成员内部类(Member Inner Class)和静态内部类(Static Inner Class)。
成员内部类: 成员内部类是一个非静态的内部类,它隐含地持有对外部类实例的引用。当创建成员内部类的实例时,实际上会与外部类的实例关联起来。这就允许成员内部类访问外部类的实例变量和方法,同时也能够访问外部类的私有成员。
public class OuterClass {
private int outerVar;
public class InnerClass {
public void doSomething() {
// 在内部类中可以访问外部类的实例变量
outerVar = 10;
}
}
}
静态内部类: 静态内部类是一个与外部类实例无关的内部类。它不隐含地持有对外部类实例的引用,因此在静态内部类中不能直接访问外部类的非静态成员。如果需要访问外部类的实例,必须通过外部类的对象来实现。
public class OuterClass {
private static int staticVar;
public static class StaticInnerClass {
public void doSomething() {
// 在静态内部类中不能直接访问外部类的实例变量
// 但可以访问外部类的静态变量
staticVar = 20;
}
}
}
总体而言,内部类能够访问外部类的实例是因为编译器会在内部类中隐式地维护一个对外部类实例的引用。这样的设计允许内部类更容易地与其外部类进行交互。
能,创建成员内部类实例需要通过外部类实例来创建
在Java中,当创建内部类的实例时,通常是要先创建外部类的实例,然后通过这个外部类实例来创建内部类实例。因此,在访问外部类实例的情况下,通常可以假定外部类实例已经存在。
例如,对于成员内部类而言,它隐含地持有对外部类实例的引用,因此在创建成员内部类实例之前,通常需要有外部类的实例存在。
public class OuterClass {
private int outerVar;
public class InnerClass {
public void doSomething() {
// 在内部类中可以访问外部类的实例变量
outerVar = 10;
}
}
public static void main(String[] args) {
OuterClass outerInstance = new OuterClass();
OuterClass.InnerClass innerInstance = outerInstance.new InnerClass();
innerInstance.doSomething();
}
}
对于静态内部类,由于它不隐含地持有对外部类实例的引用,因此在创建静态内部类实例时,不需要先创建外部类的实例。静态内部类的创建方式如下:
public class OuterClass {
private static int staticVar;
public static class StaticInnerClass {
public void doSomething() {
// 在静态内部类中不能直接访问外部类的实例变量
// 但可以访问外部类的静态变量
staticVar = 20;
}
}
public static void main(String[] args) {
OuterClass.StaticInnerClass innerInstance = new OuterClass.StaticInnerClass();
innerInstance.doSomething();
}
}
总体而言,在使用内部类的情况下,通常会有外部类实例存在或者通过外部类实例来创建内部类实例。
可以(静态内部类中是不能的)
在成员内部类中是可以修改外部类的实例的成员变量的。这是因为成员内部类会隐含地持有对外部类实例的引用。以下是一个示例:
public class OuterClass {
private int outerVar;
public class InnerClass {
public void modifyOuterVar(int newValue) {
// 在内部类中修改外部类的实例变量
outerVar = newValue;
}
}
public static void main(String[] args) {
OuterClass outerInstance = new OuterClass();
OuterClass.InnerClass innerInstance = outerInstance.new InnerClass();
System.out.println("Before modification: " + outerInstance.outerVar);
// 调用内部类的方法来修改外部类的实例变量
innerInstance.modifyOuterVar(42);
System.out.println("After modification: " + outerInstance.outerVar);
}
}
在上述例子中,InnerClass
中的 modifyOuterVar
方法可以修改外部类 OuterClass
的 outerVar
变量。
请注意,在静态内部类中是不能直接修改外部类的实例变量的,因为静态内部类没有隐含地持有对外部类实例的引用。如果需要在静态内部类中修改外部类的实例变量,需要通过外部类的实例来进行。
回到最初的问题,显然,集合在遍历时需要先创建一个容器是不正确的
ArrayList
在迭代时直接通过成员内部类就可以直接访问到外部类实例数据ArrayList.this.elementData
,通过它加上游标即可直接获取集合数据,实现遍历操作
猜想:
Fail-Fast
机制的集合迭代的时候是不会创建新容器的。也是直接取原集合中的数据。
Fail-Safe
机制的并发集合迭代的时候,可能也不需要创建新容器。
比较多的说法是并发集合会创建一个新的集合副本,再迭代,这样才能实现Fail-Safe
,但是我看ConcurrentHashMap的keys()方法,并没有。也可能是代码太长了没找到,或者版本比较新有改动。