初识集合的应用
说明:
可以先看看: java 集合详解完整版(超详细) Java 集合框架最全详解 (看这篇就够了) 各种数据结构看看:Java 集合总结,详细且易懂
面试问题可以看看: Java 集合总结
关于底层看看数据和链表区别:数组和链表的常用操作时间复杂度分析_数组,链表适用场景,时间复杂度
1.集合前置介绍
(1) 集合是一个工具类 ,其可以存储任意数量的具有共同属性的对象;
(2)集合的长度可以动态改变,这有别于数组;
(3)应用场景:
2.集合框架的体系结构
Collection:存储类的对象
(1)List接口:序列;有序,允许重复。实现类: ArrayList 。
(2)Queue接口:队列;有序,允许重复。实现类:LinkedList(LinkedList类同时也实现了List接口,表示的是链表的内容)。
(3)Set接口:集;无序,不允许重复。实现类: HashSet(哈希集) 。
Map:存储键值对
(1)实现类: HashMap(哈希表) :存储键值对。
……………………………………………………
3.集合和数组的区别
List集合体系及应用
1.List概述,仅供浏览,重点还是List的常用方法的用法
Collection接口是输入util包的,需要手动导入。
其中主要方法有:
add():添加元素的方法;clear():清空集合的方法;contains()方法:判断集合中时候有某元素的方法;isEmpty():判断集合是否为空的方法;iterrator():集合遍历方法;
size()方法:获取集合元素数量的方法等等;
……………………………………………………
List接口为Collection接口的子接口,其中新添的方法有:
get(int index)方法:返回列表中指定位置处的元素;indexOf(object o):返回某个对象在列表中的索引;sort():对列表进行排序;
……………………………………………………
ArrayList实现类的构造方法:
ArrayList常用方法见演示实例,也可查API等。
2.问题列举
(1) eclipse使用变量创建构造方法
…………………………………………………
(2) 常见的 add()方法是在list的末尾追加内容,如何在list的中间添加元素?
……………………………………………………
(3) remove() 方法
remove(int index):删除指定索引位置处的元素;
remove(Object o):移除搜索到的第一个;
……………………………………………………
(4) 修改某个元素, set()方法
Set集合体系及应用
1.Set简述
Set(接口):元素无序,不可以重复(内存地址不可重复),称为集;
常见的是实现类HashSet,即哈希集;
由于HashSet可以出现null元素,又因为其不可以重复,所以做多只能有一个null元素;
HashSet具有良好的存取和查找性能,更适用于频繁存取和查找的场景。
HashSet构造方法
2.方法,问题列举
add() 方法:Set只有这一种add()方法
取Set中元素 时,因为Set是无序的,所以没有index,此时需要使用迭代器去获取
So如何得到迭代器?→ iterator()方法
Set放到迭代器后,下一步就是遍历迭代器输出:
……………………………………………………
当向Set中添加重复元素时,不会起什么作用,只会浪费时间浪费内存。 用Set去重,以前这样用过 。
删除某个元素 :boolean remove(Object o)
boolean removeAll(Set set):删除所有集合中所有元素。
!!!注意: 当通过传统方法删除集合中满足条件的多个元素时候,会报错(因为Set为了安全,不允许在读取Set的时候再删除元素):
对于这种情况,正确的做法是:将集合中所有满足条件的元素组成一个新的子集合,然后调用集合的removeAll()方法即可,如下示例:
判断集合是否为空:isEmpty()
(1) eclipse中自动添加toString()方法
Object类中的 toString()方法用于返回对象的字符串表示,直接打印类就是调用类的toString()方法;一般我们在实际中,习惯重写toString()方法
3.hashcode
Set:不允许存放 内存地址 一样的元素。
(预先说一下(下面会有解释):向Set中存放数据时候,会先通过hashCode()方法看看,这个数据该放在那个“区块中”,然后通过equals()方法查看在该区块中是否有相同的元素)
Set中自然不能添加相同的字符串,相同的数字,在向Set中添加元素时大概率用到了自动装箱,把数字转成了包装类,而直接add(1)时,1是存放在常量池中的,所以通过add(1)形式添加时候,当数字相同时,其包装类对象就是同一个,所以在哈希算法的情况下,都是相同的,即不能重复添加。这个hashcode值的细节咱可不深究,只需要知道这是个“牛逼”的类似数据库的索引即可 。
问题:
如果,向Set中添加bean类,当new两个各属性信息一样的对象,向Set中添加的时候,是可以添加的,这个时候,看起来是向Set中添加了两个信息完全一样的对象,但其实 Set:不允许存放 内存地址 一样的元素 所以,而, _ Set中控制不可添加重复元4素的方法主要是hashcode()方法和equals()方法,所以有的时候为了防止两个信息完全一样对象重复添加的问题,需要重写这两个方法。_
一般,hashCode()方法我们维持原样不管,面也不用理解该方法的内容;equals()方法可以改一下
……………………………………………………
hashCode()方法背后有个哈希表的概念:
哈希表的简要介绍:
如果用ArrayList(没有利用哈希算法)存储100个正整数,这100个数据在内存中是连续的,当我们向查询某个数据是否在该list中时候,需要挨个比较,效率特别低。而哈希表可以解决这个问题。
那么如何使用哈希表嘞?
哈希表可以理解为通过某种规则将数据进行分类(分块),根据一种计算规则,把数据存放在内存中的不同的“区块中”,存储数据的时候什么数据存在什么“区块中”,这需要一个规则,这个规则其实是根据hashcode,具体的hashcode如何设计和得到需要根据需求来处理。一个好的hashcode的获得,需要严格的算法去完成的,对于一般情况,通过IDE自动生成的hashCode()方法生成的hashcode就够用了,直接拿来用就行了。
下面是一个简单的示例,通过hash规则如下图存放数据,这样当查询数据的时候,可以先根据计算规则,计算该数据在哪个“区块中”,然后就可以直接再在这个“区块中”,通过equals方法去搜寻,这样可以快速地的提高查询速度(不需要从头一个个比较)。以下是一个简单举例。(实际hashcode算法比除三取余复杂的多)。
如当查询97时候,通过97%3 = 1,得知97在1号筒里面,直接在1号桶里去找就行了,提高速度。
注:通过后面的泛型可以知道,一般我们在集合中存放同类的对象,所以在该对象的类中重写hashcode方法和equals方法,就可很好地完成实际的业务需求。?
……………………………………………………
由上面hash表的原理可知,两个对象hashcode相同,只能说明这两个对象是在同一个“区块中”的,那么具体如果在同一个区块中区分不同的对象?这是就需要用到符合实际需求的equals()方法。
通过这样改写equals()方法后,虽然两个对象是不同的对象(内存地址不同),但其各个属相相同,那么在该业务场景下也认为这两个对象是“同一个”对象,这样再向Set中添加该对象元素的时候,就不会出现上面描述的那个重复添加相同属性对象的不符合实际业务需求的问题了。
注:疑问,到底什么样的对象会被放在不同的内存区域范围中,查多大的对象会被放在相同的内存区域块中,这个和hashCode()方法有关。 同一个类的两个对象,这两个对象各属性完全一样,那么这两个对象的hashcode值一定相同吗(在不重写hashCode()方法的情况下)。换句话说,在存储过程中,如果hashcode值不一样,就认为不是重复元素,直接存入,用不到equals()方法了吗?还是只有在hashcode值一样的情况下,才会用到 equals()方法?待解答。****
4.Set查找和删除
contains(object o):判断集合中是否有对象o,其他搜寻的是对象时,也可以利用迭代器等,来遍历通过逻辑判断搜寻(其中的效率估计够呛)。
Set的删除见第二部分【方法,问题列举】
……………………………………………………
Map集合体系及其应用
1.Map简介
Map接口中常见方法:
常用方法有:clear()清空Map;
get(key k) :根据key获取value;
keySet():取出所有key的集合;
put(K key,V value) :向Map中添加元素;
remove(Object key):根据key删除某个键值对元素;
containsKey(Object key):判断Map中是否已有某个key;
等。
……………………………………………………
HashMap的构造方法有多种,等到用到的时候再深入研究的。
其中有一个加载因子,默认是0.75,这其中设计hash表的数据结构,可暂不深究。
2.Map常用方法
Map的定义,添加元素,遍历输出所有Value,遍历输出Key和Value;根据key获取value;
附:
(1)
(2)Scanner错误输入时候,需要next()一下,把错误结果给接收消化掉。
集合排序
疑问:存储在集合中的数据如何排序?
1)存储在集合中的基本数据类型和字符串类型如何排序?
2)存储在集合中的字符串类型如何排序?
3)自定义的类型,在集合中如何排序?(Comparator接口和Comparable接口,这两个接口中定义了比较器,自定义的类可以依照这个比较器进行排序)
回顾:以前数组排序的排序是:Arrays类的sort()方法
那么集合如何排序?
集合排序是使用Collections类中定义的sort()方法对List集合中的数据进行排序:
1.基本数据类型和字符串数据类型的排序
List中整型数据的排序:Collections类的Sort()方法:
List中String类型数据的排序:Collections类的Sort()方法:
2.自定义数据类型的集合排序:Comparator接口
使用Comparator接口或Comparable接口去定义排序的依据.
Comparator接口:
(1)Comparator接口相当于是一个比较器,其可以强行对集合中自定义数据进行排序;即一个实现了这个接口,并按照自己的逻辑重写了compare()方法的类就是一个可以使用的比较器;
(2)可以将Comparator接口(实际是是实现类这个接口的类的示例对象)作为一个参数传递给sort()方法;
即如果是对集合中的自定义数据排序的话,Comparator接口传递给collections.sort()方法;
如果是对数组中的自定义数据进行排序的话,Comparator接口传递给Arrays.sort()方法;
(3) Comparator接口中有一个 int compare(T o1,T o2)方法 :(o1和o2是需要比较的两个对象):
如果o1<o2,返回负整数;
如果o1==o2,返回0;
如果o1>o2,返回正整数;返回值是排序的依据。
(4)Comparator接口中有一个 boolean equals(Object obj) 方法:
这个是继承自Object的equals(),即被Object类中的equals()方法覆盖了,不必要重写,即不用管它。
(5)java.util包下的接口。
……………………………………………………
如下示例:集合中存储Cat类对象,按名字升序,或者,按年龄降序 :
Cat类:集合中存储对象的类型;
NameComparator类:实现Comparator接口并实现其int compare(T o1,T o2)方法,从而作为一个比较器;
CatTest类:测试类;
/*******************
注:
其排序最终还是调用Collections.sort()方法,只不过调用的是,sort()方法的另一个有两个参数的重载形式而已。
***********************************/
这儿因为业务需求,需要比较字符串,所有用到了 **String类中的 compareTo()**方法
……………………………………………………
按年龄降序排列:比较器AgeComperator类
然后将CatTest类中的Collections.sort(list, new NameComparator());改为Collections.sort(list, new AgeComparator());即可,即修改下比较器即可。
Comparable
1.Comparable接口
Comparable接口:
(1)java.lang包下的接口;
(2)Comparable接口只有一个方法:int compareTo(T o)方法;
如: obj1.compareTo(obj2):obj1小于、等于、大于obj2时,分别返回负整数、零、正整数。
(3)一个集合中的元素是某个自定类型,如果要多其排序,那么这个自定义类需要实现Comparable接口,并且按照业务需求,实现Comparable接口的compareTo(T o)方法。
(4)对于集合,其中的元素的类实现了Comparable接口的话,可以调用Collections.sort()方法完成集合的排序;
即如果是对集合中的自定义数据排序的话,使用Collections.sort()方法;
如果是对数组中的自定义数据进行排序的话,使用Arrays.sort()方法;
……………………………………………………
示例:按照价格从小到大正序排列:
Goods类:集合中存储的元素为Goods类对象;Goods类需要实现Comparable接口,并根据实际业务需要,实现Comparable接口的compareTo()方法;
GoodsTest类:测试类;
注:此时调用的sort()方法是一个参数的那个
2.compareTo()方法
可以看到,在使用Comparable接口的时候,接口的实现类实现了compareTo()方法;即这个方法是Comparable接口的compareTo()方法;
其实,对于基本数据类型的包装类,字符串类也都有compareTo()方法 。
(1)基本数据类型包装类的compareTo()方法就是比较大小的:
……………………………………………………
(2)String类的compareTo()方法,是挨个比较字符串中每个字符的Unicode值;全相等就返回0;挨个比较后,遇到小于的了就返回负数,遇到大的了就返回整数。
3.Comparator接口和Comparable接口比较
这儿都是重复阐述,看一下就行
具体在实际应用那种,选择哪种,或二者如何结合使用,需要慢慢积累。
TreeSet
由前面可知,List是有序的集合,其可以排序;而Set和Map是不涉及到排序的;
但是,除了List,Set和Map,还有一个TreeSet也是有序的,所有对于TreeSet集合也是可以进行排序的。
1.TreeSet存储String类型数据
……………………………………………………
实测发现:
2.TreeSet存储Integer型数据
实测发现:如果没有相等的,那么会返回集合中小于这个数的 最大的那一个数