Java集合其他内容和泛型

1. 集合的其他内容

1.1. Iterator

  • Iterator专门为遍历集合而生,集合并没有提供专门的遍历方法
  • Iterator是用迭代器设计模式实现的
  • Iterator的常用方法
    • boolean hasNext():判断是否存在另一个可访问的元素
    • Object next():返回要访问的下一个元素
    • void remove():删除上次访问返回的对象
  • 哪些集合可以使用Iterator遍历
    1. Collection、List、Set可以,Map不可以
    2. 提供Iterator()方法的就可以将元素交给Iterator
    3. 实现Iterator接口的集合类都可以使用迭代器遍历
  • for-each循环和Iterator的联系
    • for-each循环时(遍历集合),底层使用的是Iterator
    • 凡是可以使用for-each循环(遍历的集合),肯定也可以使用Iterator进行遍历
  • for-each循环和Iterator的区别
    • for-each还能遍历数组,Iterator只能遍历集合
    • 使用for-each遍历集合时,不能删除元素,会抛出异常ConcurrentModificationException,使用Iterator遍历时能删除元素
  • Iterator是一个接口,他的实现类在相应的集合实现类中,比如在ArrayList中存在一个内部类itr implements Iterator
  • Iterator不设计成一个类,而是接口的原因:不同的集合类,底层结构不同,迭代方式不同,所以提供一个接口,让相应的实现类来实现
public class TestIterator {
    public static void main(String[] args) {
        // 创建一个集合对象
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        System.out.println(list); // [1, 2]
        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
            int elem = it.next();
            if(elem == 2) it.remove();
        }
        System.out.println(list); // [1]
    }
}
  • Iterator是怎么工作的:不同集合的遍历由Iterator的不同实现类完成,以Iterator遍历ArrayList为例进行说明
public Iterator<E> iterator() {
    return new Itr();
}

public class Itr implements Iterator<E> {
    int cursor;
    int lastRet = -1;
    public boolean hasNext() {
        return cursor != size;
    }
    public E next() {
        int i = cursor;
        Object[] elementData = ArrayList.this.elementData;
        cursor = i + 1;
        return (E)elementData[lastRet = i];
    }
}

在这里插入图片描述

1.2. ListIterator

  • ListIterator和Iterator的关系
    • public interface ListIterator<E> extends Iterator<E>
    • 都可以遍历List
  • ListIterator和Iterator的区别
    • 使用范围不同
      • Iterator可以应用于更多的集合,Set、List和这些集合的子类型
      • ListIterator只能用于List及其子类型
    • 遍历顺序不同
      • Iterator只能向后遍历
      • ListIterator还可以逆序向前遍历
    • Iterator可以在遍历过程中remove();ListIterator可以在遍历的过程中remove()、add()、set()
    • ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现;Iterator没有此功能
public class TestListIterator {
    public static void main(String[] args) {
        // 创建一个集合对象
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        ListIterator<Integer> lit = list.listIterator();
        while (lit.hasNext()) {
            lit.next();
        }
        while (lit.hasPrevious()) {
            int elem = lit.previous();
            System.out.println(elem + " " + lit.nextIndex() + " " + lit.previousIndex()); // 1 0 -1
        }
    }
}

1.3. Collections工具类

  • 关于集合操作的工具类,比如Arrays, Math
  • 唯一的构造方法private,不允许在类的外部创建对象
  • 提供了大量的static方法,可以通过类名直接调用
public class TestCollections {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList();
        Collections.addAll(list, 5,2,3,6,1);
        System.out.println(list); // [5, 2, 3, 6, 1]
        Collections.sort(list);
        System.out.println(list); // [1, 2, 3, 5, 6]
        int index = Collections.binarySearch(list, 5);
        System.out.println(index); // 3
        int max = Collections.max(list);
        int min = Collections.min(list);
        System.out.println(max + " " + min); // 6 1
        Collections.fill(list, null);
        System.out.println(list); // [null, null, null, null, null]
        List list1 = new ArrayList();
        Collections.addAll(list1, 10,20,30);
        System.out.println(list1); // [10, 20, 30]
        Collections.copy(list, list1);
        System.out.println(list); // [10, 20, 30, null, null]
        /*
        * 在没有线程安全要求的情况下可以使用ArrayList,
        *   ArrayList线程不安全效率高,StringBuffer线程安全效率低,StringBuilder线程不安全效率高,Vector线程安全效率低
        *   方案1:程序员手动将不安全的变成安全的
        *   方案2:提供最新的线程安全并且性能高的集合类
        * */
        List list2 = new ArrayList();
        Collections.addAll(list2, 100, 200, 300, 400);
        System.out.println(list2); // [100, 200, 300, 400]
        // 将list2转换成线程安全的集合类
        list2 = Collections.synchronizedList(list2);
        // 再操作线程就安全了
    }
}

1.4. 旧的集合类

  • Vector
    • 实现原理和ArrayList相同,功能相同,都是长度可变的数组结构,很多情况下可以互用
    • 两者的主要区别
      • Vector是早期JDK接口,ArrayList是替代Vector的新接口
      • Vector线程安全效率低,ArrayList重速度轻安全,线程不安全
      • 长度需增长时,Vector默认增长一倍,ArrayList增长50%
  • Hashtable类
    • 实现原理和HashMap相同,功能相同,底层都是哈希表结构,查询速度快,很多情况下可以互用
    • 两者的主要区别
      • Hashtable是早期JDK提供,HashMap是新版JDK提供
      • Hashtable继承Dictionary类,HashMap实现Map接口
      • Hashtable线程安全,HashMap线程非安全
      • Hashtable不允许有null值,HashMap允许null值
public class TestVector {
    public static void main(String[] args) {
        Vector<Integer> v = new Vector<Integer>();
        v.addElement(1);
        v.addElement(2);
        Enumeration<Integer> en = v.elements();
        while (en.hasMoreElements()) {
            Integer elem = en.nextElement();
            System.out.print(elem + " "); // 1 2 
        }
    }
}

1.5. 新一代并发集合类

  • 早期集合类Vector、Hashtable线程都是安全的,是使用了synchronized修饰方法
  • 为了提高性能,使用ArrayList、HashMap替换,线程不安全性能好。使用Collections.synchronizedList(list)、Collections.synchronizedMap(m)解决,可以使ArrayList、HashMap线程安全,底层使用synchronized代码块锁
  • 在大量并发的情况下,提供了新的线程同步集合类,位于java.util.concurrent包下,使用Lock锁
    • ConcurrentHashMap
    • CopyOnWriteArrayList
    • CopyOnWriteArraySet

1.6. 集合常用概念辨析

  • 集合和数组的比较
    数组不是面向对象的,存在明显缺陷,集合弥补了数组的一些缺点,比数组灵活实用,大大提高了软件的开发效率,且不同的集合框架类可适用于不同场合
    1. 数组容量固定且无法动态改变,集合类容量动态改变
    2. 数组可以存放基本数据类型和引用数据类型数据,集合类只能存放引用数据类型的数据
    3. 数组无法判断其中实际存有多少元素,length只表示了array容量,集合可以判断实际存有的元素数量,对总容量不关心
    4. 集合有多种数据结构(顺序表、链表、哈希表、树等)、多种特征(是否有序、是否唯一)、不同适应场合(查询快、便于删除、有序),数组仅采用顺序表方式
    5. 集合以类的形式存在,有封装继承多态等类的特性,通过简单的方法和属性调用即可实现各种复杂操作,大大提高了软件的开发效率
  • ArrayList和LinkedList的联系和区别
    • 联系
      • 都实现了List接口
      • 有序、不唯一
    • ArrayList
      • 特点:在内存中分配连续的空间,实现了长度可变的数组
      • 优点:遍历元素和随机访问元素效率较高
      • 缺点:添加和删除需大量移动元素,效率低,按照内容查询效率低
        在这里插入图片描述
    • LinkedList
      • 特点:采用链表存储方式,底层是双向链表
      • 优点:插入、删除效率较高(前提是必须先低效率的查询。如果插入删除发生在头尾可以减少查询次数)
      • 缺点:遍历和随机访问元素效率低
        在这里插入图片描述
  • 哈希表的原理(HashMap的底层原理)
    • 哈希表的特征:快:查询快、添加快
    • 哈希表的结构
      • 最常用、最容易理解的结构(JDK1.7):数组+链表
      • JDK1.8:数组+链表/红黑树(链表长度》=8)
        在这里插入图片描述
    • 哈希表添加原理
      • 计算哈希码(hashCode())
      • 计算存储位置(存储位置就是数组的索引)
      • 存入指定位置(要处理冲突,可能重复。需借助equals进行比较)
    • 哈希表查询原理:同哈希表添加原理
    • 其他
      • hashCode()和equals()的作用
      • 如何减少冲突
      • 如何产生不同数据类型的哈希码
  • TreeMap的底层原理(红黑树的底层原理)
    • 基本特征
      • 二叉树、二叉查找树、二叉平衡树、红黑树
        在这里插入图片描述
      • 每个节点的结构
        在这里插入图片描述
    • 添加原理
      • 从根节点开始比较
      • 添加过程就是构造二叉平衡树的过程,会自动平衡
      • 平衡离不开比较:外部比较器优先,然后是内部比较器,否则出错
    • 查询基本原理同添加
  • Collection和Collections的区别
    • Collection是Java提供的集合接口,存储一组不唯一、无序的对象。他有两个子接口List和Set
    • Java中还有一个Collecions类,专门用来操作集合类,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作
  • Vector和ArrayList的联系和区别
    • 实现原理和ArrayList相同,功能相同,都是长度可变的数组结构,很多情况下可以互用
    • 两者的主要区别:
      • Vector是早期JDK接口,ArrayList是替换Vector的新接口
      • Vector线程安全效率低,ArraryList线程不安全速度高
      • 长度需增长时,Vector默认增长一倍,ArrayList增长50%
  • 各种集合类的特点
    在这里插入图片描述
类型存储结构顺序唯一性查询效率添加/删除效率
ArrayList顺序表有序(添加)不唯一索引查询效率最高效率低
LinkedList双向链表有序(添加)不唯一效率低效率高
HashSet哈希表无序唯一效率最高效率最高
HashMap哈希表Key无序key唯一效率最高效率最高
LinkedHashSet哈希表+链表有序(添加)唯一效率最高效率最高
LinkedHashMap哈希表+链表Key有序(添加)key唯一效率最高效率最高
TreeSet红黑树有序(自然)唯一效率中等效率中等
TreeMap红黑树有序(自然)key唯一效率中等效率中等
CollectionMap
无序不唯一CollectionMap.values()
唯一HashSetHashMap keySet
有序不唯一ArrayListLinkedList
唯一LinkedHashSetTreeSet
LinkedHashMap keySetTreeMap keySet

2. 泛型

2.1. 为什么需要泛型

  • 没有泛型之前
    • 不安全:添加元素是无检查->宽进
    • 繁琐:获取元素时需要强制类型转换->严出
  • 采用泛型之后
    • 安全:严进
    • 简单:宽出

2.2. 什么是泛型generic

JDK 1.5引入泛型,即参数化类型
方法定义和调用:定义方法时有形参,然后调用此方法时传递实参

  • 定义方法
public int add(int num1, int num2) {
    return num1 + num2;
}
  • 调用方法: add(10,20);
  • 泛型类的定义和创建对象
public class ArrayList<T> implements List<T> {}
List<String> list = new ArrayList<String>();

2.3. 泛型内容

  1. 泛型接口
public interface GenericInterface<T> {
    public void show(T t);
}
  1. 泛型类
public class GenericClass<T> implements GenericInterface<T> {
    @Override
    public void show(T t) {
    }
}
public class GenericClass implements GenericInterface<String> {
    @Override
    public void show(String t) {
    }
}
  1. 泛型方法
    • 泛型类中方法即使使用了泛型,也不是泛型方法;泛型方法将泛型定义在方法上
    • 静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定时,必须要将泛型定义在方法上
public class GenericTool {
    public static <T> void method1(GenericClass<T> generic) {
    }
}
  1. 上限和下限通配符
    • <? extends E>:上限通配符,用来限制类型的上限
    • <? super E>:下限通配符,用来限制类型的下限
    • 注意:此处?是类型实参,而不是类型形参。可以解决当具体类型不确定的时候,这个通配符就是?
public class GenericTool {
    public static <T> void method1(GenericClass<T> generic) {
    }
    public static <T> void method2(GenericClass<Student> generic) {
    }
    public static <T> void method3(GenericClass<? extends Student> generic) {
    }
    public static <T> void method4(GenericClass<? super Student> generic) {
    }
    public static void main(String[] args) {
        method1(new GenericClass<Date>());
        method2(new GenericClass<Student>());
        // 实参可以是Student及其子类
        method3(new GenericClass<MiddleSchoolStudent>());
        method4(new GenericClass<Person>());
        method4(new GenericClass<Object>());
    }
}

class Person() {}
class Student extends Person {}
class MiddleSchoolStudent extends Student {}

2.4. 泛型注意事项

  • 泛型提出的目的,其作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型相关信息擦出。成功编译后的class文件中,不包含泛型中的类型信息。泛型信息不会进入到运行时阶段
  • 泛型类,是在实例化类时指明泛型的具体类型
  • 泛型方法,是在调用方法时指明泛型的具体类型

3. IO流概述

3.1. IO流概述

在Java中,数据的输入/输入以流(stream)的方式进行;Java提供了各种各样流的类,用于获取不同种类的数据;程序中通过标准的方法输入输出数据;
Java流一般位于java.io包中
数据源data source,提供原始数据的媒介。常见:数据库、文件、其他程序、内存、网络连接、IO设备
在这里插入图片描述
流是一个抽象、动态的概念,是一连串连续动态的数据集合。

3.2. IO流分类

  • 按流的方向分类
    • 输入流:数据流向是数据源道程序(以InputStream、Reader结尾的流)
    • 输出流:数据流向是程序到目的地(以OutputStream、Writer结尾的流)
      在这里插入图片描述
  • 输入输出流的划分是相对程序而言的,而不是相对数据源
  • 按处理的数据单元分类
    • 字节流:以字节为单位获取数据,命名上以Stream结尾,顶级类Input/OutputStream
    • 字符流:以字符为单位获取数据,命名上以Reader/Writer结尾,顶级类FileReader/FileWriter
  • 按处理对象不同分类
    • 节点流:直接从数据源或目的地读写数据,如FileInputStream、FileReader等;处于IO操作的第一线,所有操作必须通过他们进行
    • 处理流:不直接连接到数据源或目的地,是处理流的流。通过对其他流的处理提高程序的性能。如BufferedInputStream、BufferedReader等。处理流也叫包装流;可以对节点流进行包装,提高性能或提高程序灵活性

3.3. IO流体系结构

InputStream和OutputStream

  • Java语言中最基本的两个字节输入输出类
  • 其他所有字节输入输出流类都继承自这两个类
  • 这两个类都是抽象类,不能创建他们的实例,只能使用他们的子类
    在这里插入图片描述
    在这里插入图片描述

Reader和Writer

  • Java语言中最基本的两个字符输入输出类
  • 其他所有字符输入输出流类都继承自这两个类
  • 这两个类都是抽象类,不能直接实例化,只能使用他们的子类
    在这里插入图片描述
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MingQi39

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值