java进阶 第十讲 集合与迭代器
1 集合
Collection
List Set
ArrayList
LinkedList
Set---> Map
Set本质上是Map的Key部分。Value--存的是一个常量。2 迭代器
什么是迭代器?
Iterable:可迭代的,可遍历的,也就是说实现了Iterable接口的类所产生的对象是可以被迭代的,也就是说这个对象是可以被遍历的。
循环遍历。
Iterable中的重要方法:
Iterator<T> iterator()
Iterable接口中有一个方法叫做iterator(),该方法返回值类型是Iterator<T>。
也就是说,iterator方法返回一个对象。返回的对象的类型是Iterator类型的
Iterator是一个接口,所以返回的必须是Iterator的实现类的对象。这就是多态。
父类型的引用指向子类型的对象。Iterator iter = iterator();
Iterator iter = new IteratorImpl();
研究Iterable接口,本质上就是要研究Iterator<E>接口:
boolean hasNext():意味着元素还有没有下一个,如果有就返回true,没有就返回false
E next() :取出下一个元素
因为Iterator也是接口,我们要能够使用它,就必须要有实现类。只有在实现类中才有意义。
Iterator真正被用到的地方是在哪里?也就是说Iterator接口是怎样被使用的?
其实它真正被用到的地方是在Iterable接口中。只要实现了Iterable接口的类都要实现Iterator接口中的方法。
Collection的实现类都实现了Iterable接口,因为Collection本身就是继承了Iterable接口。
也就是说:集合的实现类所产生的对象都是可以被迭代的,但是Map不是。因为Map不是集合。集合才可以被迭代,Map不能被迭代。
问,Map完全不能被迭代吗?可以迭代Key部分。Key部分就是Set集合。所以Key是可以被迭代的。2.2 怎么实现迭代器
迭代器只能在集合中使用,集合是一种数据结构。常用的包括List和Set。
比如,对于一个List来讲,实现类有ArrayList、LinkedList等等。
ArrayList要实现迭代器的迭代功能该怎么做?
public Iterator<E> iterator() {
return new Itr();
}
这个是Iterable接口中的方法,因为ArrayList实现了List接口,List接口继承自Collection
Collection继承自Iterable
所以:ArrayList一定要实现Iterable接口中所有的方法。
Iterable接口中有Iterator接口,所以ArrayList还要实现Iterator接口。
//ArrayList中Iterator的具体实现:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
// 如果cursor == size,说明遍历到最后一个元素,没有下一个元素了
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}解释说明:
Object[] elementData = ArrayList.this.elementData;
以前我们知道:类名.的方式是对于静态的属性和方法。ArrayList.this,这完全不符合类名.的方式。因为this不可能是静态的。这里的类名.的方式不是指调用方法或者是属性。而是强调。强调这个this.elementData数组是ArrayList类的成员属性。这种用法只在内部类中。防止与内部类中的属性重名,区分内部类和本类的属性或者是方法名。
如果在内部类中调用静态方法,就不会产生这个this关键字。有this就是用来区分的。
类名.this的用法表示强调,只在内部类中使用。如果,我们自己定义了一个集合,那么我们就需要实现Collection接口。
如果实现上述接口,那么就一定要自己写Iterator的实现类,该实现类一般为内部类。
因为内部类可以作为成员属性。当然也可以定义为外部类,可以使用组合、聚合等方式使用。
那么,如果我们使用API中的集合呢?
因为API提供给我们的集合都是写好了的,已经实现了Iterator接口的,那么我们只要拿到
Iterator接口的实现类的对象,就可以访问实现类中所有的方法。
使用的方式是多态,父类型的引用指向子类型的对象。然后通过父类型的引用调用子类型对象的方法。public class Test {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
for (int i = 0; i <arrayList.size(); i++ ) {
System.out.println(arrayList.get(i));
}
Iterator iterator = arrayList.iterator();// return new Itr();
//拿到arrayList中的迭代器,实际上是Itr的对象
// Iterator iterator = new Itr();
System.out.println(iterator.hasNext());
// 遍历整个ArrayList
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
// foreach 增强for循环。
for (Integer i : arrayList) {
//遍历arrayList数组中的每一个Integer类型的元素
System.out.println(i);
}
int[] ints = {1,2,3,4,5,6};
for (int anInt : ints) {//在ints这个数组中要遍历每一个anInt元素
System.out.println(anInt);
}
}
}3 总结
以后,如果是调用API中的集合,使用迭代器的时候我们可以通过iterator()直接拿到一个迭代器,通过这个迭代器可以直接迭代集合中的元素。
当然也可以使用增强for循环,也就是foreach,迭代集合中的元素。
在应用中,往往需要我们自己去定义一个集合,定义一种集合类型的数据结构,这时候我们需要自己实现Iterator接口(或者实现Iterable接口)。我们可以使用内部类的方式或者是外部类的方式。
实现了之后才能使用迭代器。
foreach增强for循环,一定是在集合中使用的,如果是自定义的,一定要实现Iterable(Iterator)接口。
如果是使用API中的集合,用到迭代器的时候,三部曲:
第一步:new一个集合对象
第二步:调用该对象的iterator()方法,返回一个迭代器对象
第三步:通过这个迭代器对象来访问hasNext()和next()方法
如果是自定义的集合,要实现Itrable接口,重写iterator()方法,返回一个Iterator对象
那么要重新定义Iterator接口的实现类,重写Iterator接口中的方法,核心是重写hasNext()和next()方法
重写方式有三种,外部类、成员内部类和局部内部类
用得比较多的且容易理解的是成员内部类。
具体的方式如下:package .generic;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* @program: Study
* @description:
* @author: tianjie
* @create: 2021-02-06 10:54
*/
public class LinkList<T> implements Iterable {
Node head;
int size = 0;
// 顺序添加,也就是在尾结点上添加元素
void add(T elem) {
if (head == null) {
head = new Node(elem, null);
}
Node current = head;
while (current.next != null) {
current = current.next;
}
current.next = new Node(elem,null);
size++;
}
public T get (T elem) {
if (head == null) {
throw new NullPointerException("链表不存在");
}
Node curr = head;
while (curr.next != null) {
if (curr.elment != elem) {
curr = curr.next;
}else if (curr.elment == elem){
break;
}
}
return (T)curr.elment;
}
@Override
public Iterator iterator() {
int cursor = 0;
//这是局部内部类的写法
return new Iterator() {
@Override
public boolean hasNext() {
return cursor != size;
}
@Override
public Object next() {
int i = cursor;
if (i >= size) {
throw new NoSuchElementException();
}
;;
return null;
}
};
}
}
// 成员内部类的方式如下:
@Override
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator {
@Override
public boolean hasNext() {
return false;// 业务逻辑自己写
}
@Override
public Object next() {
return null;// 业务逻辑自己写
}
}4 Properties
这是一个很重要的类,Properties:属性
Properties是一个特殊的Map,它的key和value都是String类型的。
Properties类表示一组持久的属性。
Properties可以保存到流中或从流中加载。 这句话很重要,这是我们以后常用到的
属性列表中的每个键及其对应的值都是一个字符串。
这个类常用来读取配置文件。
有一些常用的东西比如:IP和端口,固定的,192.168.0.1:8080
conn(IP,port)
......................
张三=192.168.0.1:8080
李四=192.168.5.1:8080
王五=192.168.8.1:8080
......................
读取上述的文件来获得其中的key和value
网站注册登录:
填账号和密码
username=“zhangsan”
passwd="123456"
mysql=3306
..........key=value
zhangsan=123
lisi=567public class Test {
public static void main(String[] args) {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("F:\\IdeaProjects\\Study\\src\\com\\tj\\propertiesTest\\pro"));
String property = properties.getProperty("2019404518");
System.out.println(property);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Properties();
try {
properties.load(new FileInputStream("F:\\IdeaProjects\\Study\\src\\com\\tj\\propertiesTest\\pro"));
String property = properties.getProperty("2019404518");
System.out.println(property);
} catch (IOException e) {
e.printStackTrace();
}
}
}
















