Java核心技术学习笔记

集合

集合接口与实现分离

1.Java集合类库将接口与实现分离。
2.队列接口指出可以在队列尾部添加元素,在队列的头部删除元素,并且可以查找队列中元素的个数,按照先进先出。
3.队列有两种实现方式:一种是使用循环数组。另一种是使用链表。循环数组要比链表更高效,但是一个有界集合,即容量有限,如果要收集的对象没有上限,就用链表实现
4.当程序使用队列时,一旦构造了集合,就不需要知道哪种实现。只要在构造集合对象时,才会使用具体的类。可以使用接口类型存放集合引用。

Collection接口

1.Collection接口有两个基本方法,add方法添加元素成功返回true,没改变集合返回false和iterator方法返回一个迭代器

迭代器

1.通过反复调用next方法,可以遍历集合中每个元素。但是,如果到达了集合的末尾,next方法将抛出一个异常。因此在调用前调用hasNext方法
2.编译器简单地将foreach循环转换为带有迭代器的循环。
3.foreach循环可以处理任何实现了Iterable接口的对象
4.可以调用forEachRemaining方法提供一个lambda表达式实现对每一个元素进行遍历
5.访问元素顺序取决于集合类型,如果集合类型为有顺序的则索引从0开始,若访问集合类型为无顺序的则无法预知访问各元素的顺序。
6.Iterator接口的remove方法将会删除上次调用next方法时放回的元素,在决定删除某个元素之前应该看一下这个元素
7.next和remove方法存在依赖性,如果没有next直接remove方法会报错

泛型实用方法

1.由于Collection与Iterator都是泛型接口,所以可以编写处理任何集合类型的使用方法
2.Collection接口声明了很多有用的方法,所有实现类都必须提供这些方法。为了让集合更容易实现这个接口。第一种做法是Java类库提供实现一些方法,然后让具体的类扩展这个类。这种做法有些过时,最好的做法是提供Collection接口的默认方法。

集合框架中的接口

java集合框架为不同类型的集合定义了大量的接口
在这里插入图片描述
1.集合有两个基本接口:Collection和Map,可以用add方法添加值,不过由于映射包含键/值对,所以要用put方法来插入
2.List是一个有序集合,可以用两种方式访问元素:使用迭代器访问,或者使用一个整数索引来访问。后面这种方法称为随机访问。
3.ListIterator接口是一个Iterator的一个子接口。它定义一个方法用于在迭代器位置前面增加一个元素
4.坦率地讲,集合框架的这个方面设计得很不好。实际上有两种有序集合,其性能开销有很大差异。由数组支持的有序集合能快速访问,因此适合提供一个整数索引来访问,与之不同,链表尽管也是有序的,但是随机访问慢,应该用迭代访问。如果原先提供两个接口就会更容易些。
5.Java1.4引入一个标记接口RandomAccess,这个接口不包括任何方法,不过可以用它来测试一个特定的集合是否支持高效随机访问。
6.Set接口等同于Collection接口,但是其方法的行为更严谨的定义。集的add方法不允许添加重复的元素。要适当定义集的equals方法:只要两个集合包含同样的元素就认为他们是相等的,而不要求这些元素有相同的顺序。hascode方法的定义要保证包含相同元素两个集合得到相同的散列码
7.从概念上讲,并不是所有集合都是集。建立一个Set接口可以允许程序员编写只接受集的方法。

具体集合

在这里插入图片描述
集合的关系在这里插入图片描述

链表

1.数组在连续的存储位置上存放对象引用,而链表则是将每个对象存放在单独的链接中。所有链表实际上都是双向连接的,即存放着序列中下个链接的引用
2.从链表中间删除一个元素是一个很轻松的操作,只需要更新所删除周围的链接即可
3.若要在链表中间添加元素,则需要用到迭代器,只用对自然有序的集合使用迭代器才有实际意义
4.由于Iterator接口中没有add方法。实际上,要提供Iterator的一个子接口ListIterator
5.ListIterator也可以用来反向遍历链表
6.当使用add操作时,新添加的元素将变成列表的新表头,当迭代器越过链表的最后一个元素时,添加的元素将成为列表的新表尾
7.如果调用了next之后,remove方法会把左侧的元素给删除,previos方法会把右侧的元素删除。不能连续调用两次remove,add方法依赖迭代器的位置,而remove方法不同,它依赖于迭代器的状态。
8.set方法用一个新元素替换调用next或previos方法返回的上一个元素。
9.如果在某个迭代器修改集合时,另一个迭代器在遍历这个集合,那么一定会出现混乱。链表迭代器设计可以检测到这种修改,会抛出异常。
10.可以根据需要为一个集合关联多个迭代器,前提是这些迭代器只能读取集合。或者,可以在关联一个同时读写的迭代器。
11.集合可以跟踪更改操作。每个迭代器都会为它负责操作维护一个单独的更改操作数。在每个迭代器方法开始处,迭代器会检查它自己的更改操作数是否与集合的操作数相等
12.并发操作有一个奇怪的例外,链表只跟踪对列表的结构性修改,如添加和删除链接。set方法不被视为结构性修改,可以为一个链表关联多个迭代器,所有迭代器都调用set方法修改现有链表的内容。
13.需要按整数索引访问元素时,程序员通常不选用链表
14.链表的get方法每一次查找一个元素都要从列表的头部中重新开始搜索
15.get方法做了一个微小的优化,如果索引大于等于size()/2,就从列表的尾端开始搜索元素
16.nextIndex方法返回下一次调用next方法时所返回的整数索引;previosIndex返回下一次调用previous方法时所返回元素的整数索引
17.list.listIterator(n)将返回一个迭代器,这个迭代器指向索引为n的元素前面的位置
18.避免使用以整数索引表示链表中位置的所有方法。如果需要对集合随机访问,就使用ArrayList,而不要使用链表。

数组列表

1.List接口用于描述一个有序集合,并且集合中每个元素的位置都很重要
2.有两种访问元素的协议:一种是通过迭代器,另一种是通过get和set方法随机访问每个元素。后者不适用于链表。
3.ArrayList类和Vector类区别是Vector类的所有方法都是同步的。可以安全地从两个线程访问一个Vector对象。但是,如果从一个线程访问Vector(这种情况更为常见),代码就会在同步操作上白白浪费大量时间。因此,建议在不需要同步时使用ArrayList,而不要使用Vector。

散列集

1.如果想要查看某个指定的元素,却又不记得它的位置,就需要访问所有元素,直到找到为止。如果集合中包含元素很多,这将会需要很长时间。如果不在意元素的顺序,有几种能够快速查找元素的数据结构。其缺点是无法控制元素的次序。这些数据结构将按照对自己最方便的方式组织元素
2.散列集可以快速地查找对象。散列表为每个对象计算一个整数,称为散列码。散列码是由对象实例字段得出的一个整数,散列码只与计算散列那个对象有关,与散列表中其他对象无关
3.散列表用链表数组实现,每个列表称为桶。要想查找表中对象的位置,就要先计算它的散列码,然后与桶的总数取余,所得到的结果就是保存这个元素桶的索引
4.当然,有时候会遇到桶已经被填充的情况。这种现象称为散列冲突。这时,需要将新对象与桶中的所有对象进行对比,查看这个对象是否已经存在。如果散列码合理随机分布,桶的数目也足够大,需要比较的次数就会很少。
5.桶满时会从链表变成平衡二叉树。如果选择的散列函数不好,会产生很多冲突,或者如果有恶意代码试图在散列表中填充多个相同散列码的值,这样改为平衡二叉树提高性能。
6.如果想更多地控制散列表的性能,可以指定一个初始的桶数,桶数是指用于收集有相同散列值的桶的数目。如果插入到散列集的元素太多,就会增加冲突数量,降低检索性能。
7.通常将桶数设置为预计元素个数的75%~150%
8.标准库使用的桶数是2的幂
9.当然,并不总是能够知道需要储存多少个元素。如果散列表太满,就需要再散列。如果要对散列表再散列,就需要创建一个桶数更多的表,并将所有元素插入到这个新表中,然后丢弃原来的表。装填因子可以确定何时对散列表进行再散列,新表的桶数是原来的两倍
10.散列表可以用于实现很多重要的数据结构。其中最简单的集类型。集是没有重复元素的元素集合。集的add方法首先在这个集中添加的对象,如果不存在,就添加这个对象。
11.HashSet它是基于散列表的集。contains方法已经重新定义,用来快速查找某个元素是否已经在集中。他只查看一个桶的元素,而不必查看集合中的所有元素
12.散列集迭代器将依次访问所有桶。由于散列将元素分散在表中,所以会以一种看起来随机的顺序访问元素。只有不关心集合中元素的顺序时才应该使用HashSet

树集

1.TreeSet类与散列集十分类似,树集是一个有序集合。可以以任意顺序将元素插入到集合中。在对集合进行遍历时,值将自动地按照排序后的顺序呈现。
2.将一个元素添加到树中要比添加到散列表中慢,但是,与检查数组或链表中的重复元素相比,使用树会快很多。
3.要使用树集,必须能够比较元素。这些元素必须实现Comparable接口,或者构造时必须提供一个Comparator

队列与双端队列

1.队列允许你高效地在尾部添加元素,并在头部删除元素。
2.双端队列允许在头部和尾部都高效地添加或删除元素。不支持在队列中添加元素。Java6引进Deque接口,ArrayDeque和LinkedList类实现了这个接口。这两个类都可以提供双端队列,其大小可以根据需要扩展。

优先队列

1.优先队列中的元素可以按照任意顺序插入,但会按照有序的顺序进行检索。也就是说,无论何时调用remove方法,总会获得当前优先队列中的最小元素。不过,优先队列并没有对所有元素进行排序。如果迭代处理这些元素,并不需要对它们进行排序。优先队列使用一个精巧且高效的数据结构,称为堆。堆是一个可以自组织的二叉树,其添加和删除操作可以让最小的元素移到根,而不必花费时间对元素进行排序
2.与TreeSet一样,优先队列既可以保存了Comparable接口的类对象,也可以保存构造器中提供的Comparator对象
3.每当启动一个新的任务时,都将优先级最高的任务从队列中删除

映射

1.集是一个集合,允许你快速地查找现有的元素。但是,要查找一个元素,需要所有要查找的那个元素的准确副本。这不是一种常见的查找方式。通常我们知道某些关键信息,希望查找与之关联的元素。映射数据结构就是为此设计的。映射用来存键/值对。
2.Java类库为映射提供了两个通用的实现:HashMap和TreeMap。这两个类都实现了Map接口。
3.散列映射对键进行散列,树映射根据键的顺序将元素组织为一个搜索树。与键的关联的值不进行散列或比较。
4.要想检索一个对象,必须使用键因此必须记住,如果映射中没有储存与给定键对应的信息,get将返回null
5.null返回值可能并不方便。有时对应没有出现在映射中的键,可以使用一个好的默认值,使用getOrDefault方法
6.键必须是唯一的,不能对同一个键存放两个值。如果对同一个键调用两次put方法,第二个值会取代第一个值。实际上,put将返回与这个参数关联的上一个值。

更新映射条目

1.处理映射的一个难点就是更新映射条目。正常情况下,可以得到与键关联的原值,完成更新,再放回更新后的值。不过,必须考虑一个特殊情况,即键第一次出现。在这种情况下get会返回null,因此会出现一个NullPointerException异常。一种简单的补救就是使用getOrDefalut方法。另一种方法是首先调用putIfAbsent方法。只有当键原先存在时才会放入一个值。不过可以做的更好。merge方法可以简化这个常见的操作。如果键原先不存在,下面的调用:counts.merge(wrod,Integer::summ);将把wrod与1关联,否则使用Interger::sum函数组合原值和1

映射视图

1.集合框架不认为映射本身是一个集合。(其他数据结构认为映射是一个键/值对集合,或者是按索引的值集合
2.可以得到映射的视图(view)-这是实现了Collection接口或某个子接口的对象
3.有三种视图:键集,值集合(不是一个集)以及键/值集。键和键/值可以构成一个集,因为映射中一个键只能有一个副本。
4.Set keySet()、Collection values()、set<Map.Entry<K,V>> entrySet()会分别返回这3个视图(映射条目集的元素是实现了Map.Entry接口的类的对象)
5.keySet不是HashSet或TreeSet,而是实现了Set接口的另外某个类的类对象。
6.如果在键集视图上调用迭代器remove方法,实际上会从映射中删除这个键和它关联的值。不过,不能向键集视图中添加元素。会抛出一个异常。

弱散列映射

1.如果有一个值,它对应的键已经不再程序中的任何地方使用。假定最后一个键的引用已经消失,那么不再有任何途径可以引用这个值得对象了。但是,由于程序中的任何部分不会再有这个键,所以无法删除这个键值对。这时我们可以使用WeakHashMap。当对键的唯一引用来自散列表映射条目时,这个数据结构将与垃圾回收工作一起删除键/值对

链接散列表与映射

1.LinkedHashSet和LinkedHashMap类会记住插入元素项的顺序。这样就可以避免散列表中的项看起来顺序是随机的
2.可以用迭代器来访问这些元素,或者可以用访问顺序而不是插入顺序来迭代处理映射条目。每次调用get或put时,受到影响的项将从当前位置删除,并放到项链表的尾部(只影响项在链表中的位置,而散列表的桶不会受到影响)

枚举类与映射

1.EnumSet是一个枚举类型元素集的高效实现。由于枚举类型只有限个实例,所以EnumSet内部用位序列实现。如果对应在集中,则相应的位置为1。
2.EnumMap是一个键类型为枚举类型的映射。它可以直接高效地实现为一个值数组。

标识散列映射

1.类IdentityHashMap有特俗的用途。在这个类中,键的散列值不是用hashCode函数计算的,而是用System.identityHashCode方法计算的。它会根据对象的内存地址值计算散列码。而且,在对两个对象进行比较时,IdentityHashMap类使用==,而不使用equals。即使内容相同,也被视为不同的对象。这个类非常有用,可以用来根据哪些对象已经遍历过。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值