热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java编程思想持有对象总结

如果一个程序只包含固定数量的且其生命期都是已知的对象那么这是一个非常简单的程序泛型和类型安全的容器使用JavaSE5之前的容器的一个主要问题就是编译器允许你向容器中插入不正确的类

如果一个程序只包含固定数量的且其生命期都是已知的对象 那么这是一个非常简单的程序

泛型和类型安全的容器
使用Java SE5之前的容器的一个主要问题就是编译器允许你向容器中插入不正确的类型
在本例中 Apple和Orange都放置在了容器中 然后将它们取出 正常情况下 Java编译器会报告警告信息 因为这个示例没有使用泛型 在这里 我们使用Java SE5所特有的注解来抑制了警告信息 注解以 @ 符号开头 可以接受参数 这里的@SuppressWarnings注解及其参数表示只有有关 不受检查的异常 的警告信息应该被抑制
在这里插入图片描述

要想定义用来保存Apple对象的ArrayList 你可以声明ArrayList 而不仅仅只是ArrayList 其中尖括号括起来的是类型参数(可以有多个) 它指定了这个容器实例可以保存的类型 通过使用泛型 就可以在编译期防止将错误类型的对象放置到容器中
在这里插入图片描述
在这里插入图片描述
现在 编译器可以阻止你将Orange放置到apples中 因此它变成了一个编译期错误 而不再是运行时错误

当你指定了某个类型作为泛型参数时 你并不仅限于只能将该确切类型的对象放置到容器中 向上转型也可以像作用于其他类型一样作用于泛型
在这里插入图片描述
程序的输出是从Object默认的toString()方法产生的 该方法将打印类名 后面跟随该对象的散列码的无符号十六进制表示(这个散列码是通过hashCode()方法产生的)

基本概念
Java容器类类库的用途是 保存对象 并将其划分为两个不同的概念


  1. Collection 一个独立元素的序列 这些元素都服从一条或多条规则 List必须按照插入的顺序保存元素 而Set不能有重复元素 Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)
  2. Map 一组成对的 键值对 对象 允许你使用键来查找值 ArrayList允许你使用数字来查找值 因此在某种意义上讲 它将数字与对象关联在了一起 映射表允许我们使用另一个对象来查找某个对象 它也被称为 关联数组 因为它将某些对象与另外一些对象关联在了一起 或者被称为 字典 因为你可以使用键对象来查找值对象 就像在字典中使用单词来定义一样 Map是强大的编程工具

添加一组元素
Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数) 并将其转换为一个List对象 Collection.addAll()方法接受一个Collection对象 以及一个数组或是一个用逗号分隔的列表 将元素添加到Collection中
在这里插入图片描述
Collection的构造器可以接受另一个Collection 用它来将自身初始化 因此你可以使用ArrayList.List()来为这个构造器产生输入 但是 Collection.addAll()方法运行起来要快得多 而且构建一个不包含元素的Collection 然后调用Collection.addAll()这种方式很方便 因此它是首选方式
Collection.addAll()成员方式只能接受另一个Collection对象作为参数 因此它不如ArrayList.asList()或Collection.addAll()灵活 这两个方法使用的都是可变参数列表
你也可以直接使用ArrayList.asList()的输出 将其当作List 但是在这种情况下 其低层表示的是数组 因此不能调整尺寸 如果你试图用add()或delete()方法在这种列表中添加或删除元素 就有可能会引发去改变数组尺寸的尝试 因此你将在运行时获得 Unsupported Operation(不支持的操作)错误
Arrays.asList()方法的限制是它对所产生的List的类型做出了最理想的假设 而并没有注意你对它会赋予什么样的类型 有时这就会引发问题
在这里插入图片描述
当试图创建snow2时 Arrays.asList()中只有Powder类型 因此它会创建List而不是List 尽管Collection.addAll()工作的很好 因为它从第一个参数中了解到了目标类型是什么
正如你从创建snow4的操作中所看到的 可以在Arrays.asList()中间插入一条 线索 以告诉编译器对于由Arrays.asList()产生的List类型 实际的目标类型应该是什么 这称为显式类型参数说明

容器的打印
你必须使用Arrays.toString()来产生数组的可打印表示 但是打印容器无需任何帮助
在这里插入图片描述
在这里插入图片描述
这里展示了Java容器类库中的两种主要类型 它们的区别在于容器中每个 槽 保存的元素个数 Collection在每个槽中只能保存一个元素 此类容器包括 List 它以特定的顺序保存一组元素 Set 元素不能重复 Queue 只允许在容器的一 端 插入对象 并从另外一 端 移除对象 Map在每个槽内保存了两个对象 即键和与之相关联的值

HashSet与HashMap提供了最快的查找技术 没有按照任何明显的顺序来保存元素 TreeSet与TreeMap按照比较结果的升序保存对象 LinkedHashSet与LinkedHashMap按照被添加的顺序保存对象

Map(也被称为关联数组)使得你可以用键来查找对象 就像一个简单的数据库 键所关联的对象称为值 对于每一个键 Map只接受存储一次

List
List承诺可以将元素维护在特定的序列中 List接口在Collection的基础上添加了大量的方法 使得可以在List的中间插入和移除元素
有两种类型的List


  • 基本的ArrayList 它长于随机访问元素 但是在List的中间插入和移除元素时较慢
  • LinkedList 它通过代价较低的在List中间进行的插入和删除操作 提供了优化的顺序访问 LinkedList在随机访问方面相对比较慢 但是它的特性集较ArrayList更大

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
与数组不同 List允许在它被创建之后添加元素 移除元素 或者自我调整尺寸 这正是它的重要价值所在 一种可修改的序列

迭代器
迭代器(也是一种设计模式)是一个对象 它的工作是遍历并选择序列中的对象 而客户端程序员不必知道或关心该序列底层的结构 此外 迭代器通常被称为轻量级对象 创建它的代价小 因此 经常可以见到对迭代器有些奇怪的限制
例如 Java的Iterator只能单向移动 这个Iterator只能用来


  1. 使用方法iterator()要求容器返回一个Iterator Iterator将准备好返回序列的第一个元素
  2. 使用next()获得序列中的下一个元素
  3. 使用hasNext()检查序列中是否还有元素
  4. 使用remove()将迭代器新近返回的元素删除

在这里插入图片描述
Iterator还可以移除由next()产生的最后一个元素 这意味着在调用remove()之前必须先调用next()

现在考虑创建一个display()方法 它不必知晓容器的确切类型
在这里插入图片描述
display()方法不包含任何有关它所遍历的序列的类型信息 而这也展示了Iterator的真正威力 能够将遍历序列的操作与序列底层的结构分离 正由于此 我们有时会说 迭代器统一了对容器的访问方式

ListIterator
ListIterator是一个更加强大的Iterator的子类型 它只能用于各种List类的访问 尽管Iterator只能向前移动 但是ListIterator可以双向移动 它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引 并且可以使用set()方法替换它访问过的最后一个元素 你可以通过调用listIterator()方法产生一个指向List开始处的ListIterator 并且还可以通过调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素处的ListIterator
在这里插入图片描述
Pet.randomPet()方法用来替换在列表中从位置3开始向前的所有Pet对象

LinkedList
LinkedList也像ArrayList一样实现了基本的List接口 但是它执行某些操作(在List的中间插入和移除)时比ArrayList更高效 但在随机访问操作方面却要逊色一些
LinkedList还添加了可以使其用作栈 队列或双端队列的方法
在这里插入图片描述

Stack
栈 通常是指 后进先出(LIFO) 的容器 有时栈也被称为叠加栈 因为最后 压入 栈的元素 第一个 弹出 栈
LinkedList具有能够直接实现栈的所有功能的方法 因此可以直接将LinkedList作为栈使用
在这里插入图片描述
类名之后的告诉编译器这将是一个参数化类型 而其中的类型参数 即在类被使用时将会被实际类型替换的参数 就是T 大体上 这个类是在声明 我们在定义一个可以持有T类型对象的Stack Stack是用LinkedList实现的 而LinkedList也被告知它将持有T类型对象

如果你只需要栈的行为 这里使用继承就不合适了 因为这样会产生具有LinkedList的其他所有方法的类
在这里插入图片描述
如果你想在自己的代码中使用这个Stack类 当你在创建其实例时 就需要完整指定包名 或者更改这个类的名称 否则 就有可能与java.util包中的Stack发生冲突
在这里插入图片描述
在这里插入图片描述
尽管已经有了java.util.Stack 但是LinkedList可以产生更好的Stack 因此net.mindview.util.Stack所采用的方式更是可取的
你还可以通过显示的导入来控制对 首选 Stack实现的选择
import net.mindview.util.Stack;
现在 任何对Stack的引用都将选择net.mindview.util版本 而在选择java.util.Stack时 必须使用全限定名称

Set
Set不保存重复的元素 如果你试图将相同对象的多个实例添加到Set中 那么它就会阻止这种重复现象 Set中最常被使用的是测试归属性 你可以很容易地询问某个对象是否在某个Set中 正因如此 查找就成为了Set中最重要的操作 因此你通常都会选择一个HashSet的实现 它专门对快速查找进行了优化
Set是基于对象的值来确定归属性的
在这里插入图片描述
TreeSet将元素存储在红-黑树数据结构中 而HashSet使用的是散列函数 LinkedHashSet因为查询速度的原因也使用了散列 但是看起来它使用了链表来维护元素的插入顺序
如果你想对结果排序 一种方式是使用TreeSet来代替HashSet
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
TextFile继承自List 其构造器将打开文件 并根据正则表达式 \W+ 将其断开为单词 这个正则表达式表示 一个或多个字母 所产生的结果传递给了TreeSet的构造器 它将把List中的内容添加到自身中 由于它是TreeSet 因此其结果是排序的 在本例中 排序是按字典序进行的 因此大写和小写字母被划分到了不同的组中 如果你想要按照字母序排序 那么可以向TreeSet的构造器传入String.CASE_INSENTIVE_ORDER比较器(比较器就是建立排序顺序的对象)
在这里插入图片描述

Map
将对象映射到其他对象的能力是一种解决编程问题的杀手锏 例如 考虑一个程序 它将用来检查Java的Random类的随机性 理想状态下 Random可以将产生理想的数字分布 但要想测试它 则需要生成大量的随机数 并对落入各种不同范围的数字进行计数 Map可以很容易地解决该问题 在本列中 键是由Random产生的数字 而值是该数字出现的次数
在这里插入图片描述
下面的示例允许你使用一个String描述来查找Pet 它还展示了你可以使用怎样的方法通过使用containsKey()和containsValue()来测试一个Map 以便查看它是否包含某个键或某个值
在这里插入图片描述
Map与数组和其他的Collection一样 可以很容易地扩展到多维 而我们只需将其值设值为Map(这些Map的值可以是其他容器 甚至是其他Map) 因此 我们能够很容易地将容器组合起来从而快速地生成强大的数据结构 例如 假设你正在跟踪拥有多个宠物的人 你所需只是一个Map
在这里插入图片描述
在这里插入图片描述
Map可以返回它的键的Set 它的值的Collection 或者它的键值对的Set keySet()方法产生了由在petPeople中的所有键组成的Set 它在foreach语句中被用来迭代遍历该Map

Queue
队列是一个典型的先进先出(FIFO)的容器 即从容器的一端放入事物 从另一端取出 并且事物放入容器的顺序与取出的顺序是相同的 队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径
LinkedList提供了方法以支持队列的行为 并且它实现了Queue接口 因此LinkedList可以用作Queue的一种实现 通过将LInkedList向上转型为Queue
在这里插入图片描述
在这里插入图片描述
Queue接口窄化了对LinkedList的方法的访问权限 以使得只有恰当的方法才可以使用 因此 你能够访问的LinkedList的方法会变少(这里实际上可以将queue转型回LinkedList 不鼓励这么做)

PriorityQueue
先进先出描述了最典型的队列规则 队列规则是指在给定一组队列中的元素的情况下 确定下一个弹出队列的元素的规则 先进先出声明的是下一个元素应该是等待时间最长的元素
优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)
当你在PriorityQueue上调用offer()方法来插入一个对象时 这个对象会在队列中被排序 默认的排序将使用对象在队列中的自然顺序 但是你可以通过提供自己的Comparator来修改这个顺序 PriorityQueue可以确保当你调用peek() poll()和remove()方法时 获取的元素将是队列中优先级最高的元素
在这里插入图片描述
在这里插入图片描述
如果你想在PriorityQueue中使用自己的类 就必须包括额外的功能以产生自然顺序 或者必须提供自己的Comparator

Collection和Iterator
Collection是描述所有序列容器的共性的根接口 它可能会被认为是一个 附属接口 即因为要表示其他若干个接口的共性而出现的接口 另外 java.util.AbstractCollection类提供了Collection的默认实现 使得你可以创建AbstractCollection的子类型 而其中没有不必要的代码重复

实现Collection就意味着需要提供iterator()方法
在这里插入图片描述

当你要实现一个不是Collection的外部类时 由于让它去实现Collection接口可能非常困难或麻烦 因此使用Iterator就会变得非常吸引人
在这里插入图片描述

如果你实现Collection 就必须实现iterator() 并且只拿实现iterator()与继承AbstractCollection相比 花费的代价只有略微减少 但是 如果你的类已经继承了其他的类 那么你就不能再继承AbstractCollection了 在这种情况下 要实现Collection 就必须实现该接口中的所有方法 此时 继承并提供创建迭代器的能力就会显得容易得多了
在这里插入图片描述
在这里插入图片描述
生成Iterator是将队列与消费队列的方法连接在一起耦合度最小的方式 并且与实现Collection相比 它在序列类上所施加的约束也少得多

Foreach与迭代器
foreach语法主要用于数组 但是它也可以应用于任何Collection对象
在这里插入图片描述
之所以能够工作 是因为Java SE5引入了新的被称为Iterable的接口 该接口包含一个能够产生Iterator的iterator()方法 并且Iterable接口被foreach用来在序列中移动 因此如果你创建了任何实现Iterable的类 都可以将它用于foreach语句中
在这里插入图片描述
在这里插入图片描述
在Java SE5中 大量的类都是Iterable类型 主要包括所有的Collection类(但是不包括各种Map) 下面的代码可以显示所有的操作系统环境变量
在这里插入图片描述

foreach语句可以用于数组或其他任何Iterable 但是这并不意味着数组肯定也是一个Iterable 而任何自动包装也不会自动发生
在这里插入图片描述
尝试把数组当作一个Iterable参数传递会导致失败 这说明不存在任何从数组到Iterable的自动转换 你必须手工执行这种转换

适配器方法惯用法
如果现有一个Iterable类 你想要添加一种或多种在foreach语句中使用这个类的方法 应该怎么做呢 例如 假设你希望可以选择以向前的方向或是向后的方向迭代一个单词列表 如果直接继承这个类 并覆盖iterator()方法 你只能替换现有的方法 而不能实现选择
一种解决方案是所谓适配器方法的惯用法
在这里插入图片描述

通过使用这种方式 可以在IterableClass.java示例中添加两种适配器方法
在这里插入图片描述
从输出中可以看到 Collection.shuffle()方法没有影响到原来的数组 而只是打乱了shuffled中的引用 之所以这样 只是因为randomized()方法用一个ArrayList将Arrays.asList()方法的结果包装了起来 如果这个由Arrays.asList()方法产生的List被直接打乱 那么它就会修改底层的数组 就像下面这样
在这里插入图片描述
在这里插入图片描述
在第一种情况中 Arrays.asList()的输出被传递给了ArrayList()的构造器 这将创建一个引用ia的元素的ArrayList 因此打乱这些引用不会修改该数组 但是 如果直接使用Arrays.asList(ia)的结果 这种打乱就会修改ia的顺序 意识到Arrays.asList()产生的List对象会使用底层数组作为其物理实现是很重要的 只要你执行的操作会修改这个List 并且你不想原来的数组被修改 那么你就应该在另一个容器中创建一个副本

总结
Java提供了大量持有对象的方式


  1. 数组将数字与对象联系起来 它保存类型明确的对象 查询对象时 不需要对结果做类型转换 它可以是多维的 可以保存基本类型的数据 但是 数组一旦生成 其容量就不能改变
  2. Collection保存单一的元素 而Map保存相关联的键值对 有了Java的泛型 你就可以指定容器中存放的对象类型 因此你就不会将错误类型的对象放置到容器中 并且在从容器中获取元素时 不必进行类型转换 各种Collection和各种Map都可以在你向其中添加更多的元素时 自动调整尺寸 容器不能持有基本类型 但是自动包装机制会仔细地执行基本类型到容器中所持有的包装器类型之间的双向转换
  3. 像数组一样 List也建立数字索引与对象的关联 因此 数组和List都是排好序的容器 List能够自动扩充容量
  4. 如果要进行大量的随机访问 就使用ArrayList 如果要经常从表中间插入或删除元素 则应该使用LinkedList
  5. 各种Queue以及栈的行为 由LinkedList提供支持
  6. Map是一种将对象(而非数字)与对象相关联的设计 HashMap设计用来快速访问 而TreeMap保持 键 始终处于排序状态 所以没有HashMap快 LinkedHashMap保持元素插入的顺序 但是也通过散列提供了快速访问能力
  7. Set不接受重复元素 HashSet提供最快的查询速度 而TreeSet保持元素处于排序状态 LinkedHashSet以插入顺序保存元素
  8. 新程序中不应该使用过时的Vector Hashtable和Stack

简单的容器分类
在这里插入图片描述
点线框表示接口 实线框表示普通的(具体的)类 带有空心箭头的点线表示一个特定的类实现了一个接口 空心箭头表示某个类可以生成箭头所指向类的对象 例如 任意的Collection可以生成Iterator 而List可以生成ListIterator(也能生成普通的Iterator 因为List继承自Collection)

下面的示例展示了各种不同的类在方法上的差异
在这里插入图片描述
在这里插入图片描述
可以看到 除了TreeSet之外的所有Set都拥有与Collection完全一样的接口 List和Collection存在着明显的不同 尽管List所要求的方法都在Collection中 另一方面 在Queue接口中的方法都是独立的 在创建具有Queue功能的实现时 不需要使用Collection方法 最后 Map和Collection之间的唯一重叠就是Map可以使用entrySet()和values()方法来产生Collection


推荐阅读
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • Givenasinglylinkedlist,returnarandomnode'svaluefromthelinkedlist.Eachnodemusthavethe s ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
author-avatar
假爱多慌乱
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有