集合
- Java中的集合是工具类,可以存储任意数量的共同属性的数据。
- 数组只能用于存储固定长度数据的场景,而集合可以存储任意长度的数据
集合的应用场景
- 无法预测存储数据的数量
- 同时存储具有一对一关系的数据
- 需要进行数据的增删
- 数据重复问题
集合的体系架构
Collection
存储类的对象,例如,学生类的信息;Map
主要是以键值对的形式存储信息,例如商品和商品的价格。
List
、Queue
、Set
是三个子接口。List
、Queue
是有序和允许重复的;Set
是无序的且不可重复的。
ArrayList
、LinkedList
、HashSet
是三个实现类,实现了三个对应的接口。
Map
的主要实现类是HashMap
。
List
(列表)
List
是元素有序并且可以重复的集合,称为序列;
List
可以精确地控制每一个元素的插入位置,或删除某个位置的元素;
List
的两个主要实现类是ArrayList
和LinkedList
ArrayList
ArrayList
底层是由数组实现的;
- 元素有序且可以重复;
- 动态增长,以满足应用程序的需求;
- 在列表尾部插入或删除数据非常有效;
- 更适合查找和更新元素;
ArrayList
中的元素可以为null
。
关于接口引用指向实现类对象的说明
- 这实际上是一种多态的思想。多态的定义:指的是允许不同类的对象对同一个消息做出响应,即同一个消息可以根据发送对象的不同而采取多种不同的行为方式。(发送消息就是函数调用);
List list
是在栈区开辟一个空间放list
的引用,并没有创建对象所以不知道真正的对象是ArrayList
还是LinkedList
。当list = new ArrayList();
的时候就创建了ArrayList对象,并且把开始创建的list
引用指向这个对象。需要强调,ArrayList
和LinkedList
都是List
的实现类;
- 那么为什么一般都使用
List list = new ArrayList();
而不用ArrayList list = new ArrayList();
呢?因为List
这个接口有多个实现类,如LinkedList
或者Vector
等等,现在你用的是ArrayList
实现类,有可能之后需求更改需要使用其他的实现类了。如果使用的是前一种方法,那么只需要改变一开始的引用赋值的那一行代码就可以了,其他使用到list
对象的代码根本不需要改动。假设如果你一开始就是用了第二种方法,尤其是你还是用了ArrayList
这个实现类特有的方法,那么就需要更改非常多的代码,对程序的重构是没有好处的。因此,一般没有特别需求的情况下,使用List list = new ArrayList();
是比较方便程序代码的重构的,这就是所谓的面向接口编程的好处。注意:如果采用了List list = new ArrayList();
的方法创建实例对象,那么ArrayList
这个实现类自己特有的方法是不可以被访问到的;
- 接口的灵活性就在于“规定了一个类必须做什么,而不管你如何做”。我们可以定义一个接口类型的实例,当引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和超类对象引用访问子类对象的机制是类似的。
Set
&HashSet
- 元素无序且不可以重复的集合,被称为集;
HashSet
是Set
的一个重要实现类,称为哈希集;
HashSet
中的元素无序且不可以重复;
HashSet
只允许一个null
元素;
- 具有良好的存取和查找性能。
Iterator
(迭代器)
Iterator
接口可以以统一的方式对各种集合元素进行遍历;
hasNext()
方法检测集合中是否还有下一个元素;
next()
方法返回集合中的下一个元素。
- 集合在进行遍历的时候是不允许进行元素的添加和删除的,如果要添加和删除需要在迭代循环的时候删除元素之后退出循环。
- 使用迭代器遍历元素的示例代码如下:
Iterator it= set.iterator();
while(it.hasNext()){
System.out.print(it.next()+" ");
}
hashCode()
与equal()
方法的重写
- 重写这两个方法主要用在
Hash
相关的集合中,主要定义hash码的产生方式以及如何判定对象是否相等,重写这个方法可以定制Set的判定相等模式。
- 以下代码为上述两个方法重写的示例,在IDEAJ中可以通过自动插入方式自动编写这两个方法。
@Override
public boolean equals(Object o) {
if(this==o)
return true;
if(o.getClass()==Cat.class){
Cat cat=(Cat)o;
return cat.getName().equals(this.getName())
&&cat.getMonth()==this.getMonth()
&&cat.getSpecies().equals(this.getSpecies());
}
else
return false;
}
@Override
public int hashCode() {
return Objects.hash(name, month, species);
}
泛型的概念
- JavaSE 5.0之后引入的概念;
- 利用对
Object
类型的引用实现参数类型的任意化,任意化的缺点就是要做显式的类型转换,这对于后期维护代码和修改代码来说带来了比较大的工作量。并且制定类型还会产生运行时错误,这是我们不希望看到的。使用泛型,就可以独立于特定数据类型来一次性定义算法,然后把算法应用于各种数据类型,而不需要做额外的工作。因此Java中定义了泛型的概念。
- Java中泛型的概念和C++中模板的区别:C++为每一种数据类型都构建了底层代码,而Java只构建了一份代码,当需要另一种类型时,Java会重新构建代码。所以C++模板的代码体积比较大,但是由于代码已经全部构建好了,因此C++的执行速度更快。
- 泛型的写法
Set<Cat> set = new HashSet<Cat>();
Iterator<Cat> it = set.iterator();
Map
Map
中的数据是以键值对(key-value)的形式存储的;
- key-value是以
Entry
类型的对象实例存在的,Entry
是接口类;
- 可以通过
key
值快速查找value
;
- 一个映射不能包含重复的键
- 每个键最多只能映射到一个值
HashMap
- 基于哈希表的
Map
接口的实现;
- 允许使用
null
值和null
键,一个Map
中只能有一个null
键;
key
值不允许重复;
- HashMap中的Entry对象是无序排列的。
增强型for循环的使用
- 我们也可以用增强型for循环来代替迭代器去遍历集合。
for(String str:set){
if(str.equals("你好"))
System.out.println("已经找到的对应的字符串");
}