1 数组和集合的对比
数组 | 集合 |
---|---|
可以存储基本数据类型和对象 | 只能存储对象(可用包装类的形式存储基本数据类型) |
长度固定 | 长度动态改变 |
定义时必须指定元素类型 | 默认Object类型 |
无法直接获取实际存储元素个数 | 可直接通过size()方法获取元素个数 |
有序的分配连续空间 | 多个方式适应不同的场合 |
2 List
List的特点是:有序、元素可重复
实现类:ArrayList、LinkedList、Vector、Stack
2.1 ArrayList
ArrayList(Array数组,List列表),实现了长度可变的数组,本质上还是使用数组实现,所以和数组的特点类似。
2.1.1 特点
- 其中的元素是有顺序的,按照元素添加的顺序排列
- 默认长度为10,内部会自动增大长度
- 允许元素重复添加
- 属于泛型,如果不指明具体的类型默认为Object,此时可以添加任意类型的元素。
- 如果指定具体的类型则只能添加对应类型的元素
2.1.2 常用方法
方法名 | 作用 |
---|---|
add(Object o) | 默认在末尾添加元素 |
add(int index, Object o) | 在指定位置添加元素 |
set(int index, Object o) | 修改指定位置的元素,返回被修改之前的元素 |
remove(Object o) | 根据元素值删除,返回boolean值表示是否成功 |
remove(int index,Object o) | 删除指定位置的元素,返回被删除的元素 |
get(int index) | 根据位置获取对应的元素 |
size() | 返回集合中的元素个数 |
isempty() | 检查集合是否为空(实际就是判断 size==0) |
indexOf(Object o) | 返回要查找元素的位置(不存在就返回-1) |
contains(Object o) | 返回boolean值表示是否存在要查找的元素(实际就是判断 indexOf >= 0) |
clear() | 清空集合 |
toArray() | 将集合转换成Object数组 |
asList(T…a) | 将数组转换成集合(静态方法,可用于创建ArrayList对象时使用) |
2.1.3 遍历元素方式
//普通循环
for(int i=0; i<list.size(); i++) {
System.out.print(list.get(i) + "\t");
}
//增强for循环
for(Integer num: list) {
System.out.print(num + "\t");
}
//迭代器遍历
Iterator<Integer> iterator = list.iterator() ; //创建迭代器对象
while(iterator.hasNext()) { //判断是否存在下一个元素
Integer num = iterator.next() ; //提取当前元素
System.out.print(num + "\t");
}
其中的迭代器遍历方式,如果学过软件设计模式就知道,其中有一个迭代器模式。将要遍历的集合交给迭代器来处理,使内部数据不会暴露,而且可以根据自己的需求创建不同的迭代器从而实现不同的遍历方式。
2.2 Vector
和ArrayList相似,都是用数组实现,基本方法也类似。
2.2.1 Vector和ArrayList的区别
Vector | ArrayList |
---|---|
同步,线程安全 | 异步,线程不安全 |
执行效率低 | 相较于Vector执行效率高 |
遍历方式:迭代器遍历、枚举遍历 | 遍历方式:迭代器遍历 |
查看源码可知在Vector中的大多数方法都是被synchronized修饰的,这个单词翻译过来就是 “同步” 的意思。被synchronized修饰的方法能够保证线程的安全。
2.2.2 枚举遍历元素
//使用枚举遍历
Enumeration<Integer> e = list.elements() ;
while(e.hasMoreElements()) { //判断是否存在下一个元素
Integer now = e.nextElement() ; //提取当前元素
System.out.print(now + "\t");
}
枚举遍历和迭代器遍历类似,但枚举遍历是比较老的技术已过时,最好使用迭代器遍历,用Iterator替代Enumeration
2.2.3 使用Vector建议
Vector已过时,不建议使用。如果需要考虑安全性的地方,作为代替可以使用Collections类中的synchronizedList(List list)方法将集合变为一个线程安全的集合
LinkedList
当需要频繁插入和删除元素时适用,本质上使用双向链式存储。具有List的所有方法。实现了队列Queue接口
特有方法 | 作用 |
---|---|
addFirst(Object o) | 在首部添加元素 |
addLast(Object o) | 在末尾添加元素,效果和默认的add()效果一样 |
getFirst() | 返回第一个元素 |
getLast() | 返回最后一个元素 |
removeFirst() | 删除第一个元素 |
removeLast() | 删除最后一个元素 |
2.3 LinkList
LinkedList和ArrayList的最大区别就是ArrayList基于数组实现,而LinkedList是基于链表实现的。所以当需要进行大量插入、删除等操作时,可以选择使用LinkList
3 Set
Set的特点是:
- 无序(元素顺序与放入顺序无关),从而不能按索引访问元素(没有get(int index)方法)
- 不可重复
实现:HashSet、TreeSet
3.1 HashSet
3.1.1 特点
- 基于哈希算法的集合,以哈希表的形式存储
- 由于根据哈希算法进行快速查找,则操作速度快,效率高
- 添加相同元素时只会添加一次,多余的添加不进去
- 根据地址判断两个元素是否相同而不是根据数据值判断
3.1.2 常用方法
由于Set也是实现了Collection接口,所以其中的方法都存在,例如size()、clear()、remove()、contains()等等
方法名 | 作用 |
---|---|
addAll(Collection c) | 将List转换成Set(可用来去重) |
3.1.3 判断元素是否重复的过程:
- 添加元素时自动调用要存入对象的hashCode()方法,得到hashCode值(若没有重写hashCode()方法,在Object类中就是返回内存地址)
- 根据hashCode值,通过哈希算法确定在哈希表中存放的位置
- 判断该位置是否已经有元素,没有元素就直接放入,若有元素就调用equals()方法进行比较,返回true则认为是重复元素,直接舍弃,否则在当前位置以链表的形式追加
3.2 TreeSet
3.2.1 特点
- 用于对元素进行排序,但仍然满足元素顺序和放入顺序无关的特点
- 基于二叉树的集合
3.2.2 排序依据:
- 元素类型本身具有自然顺序就按此排序,例如数字有大小顺序,字符串有字典序
- 元素类型没有自然顺序,直接将元素添加到TreeSet集合中会报错
则要让被排序的对象实现comparable接口,让其具有可比较性,并实现其中的compareTo()方法。或者定义一个比较器(实现Comparator接口的类),并实现compare()方法,然后在定义TreeSet对象的时候将比较器作为参数传入。若同时使用这两种方法则只会选择比较器的方式排序。
public class TreeSet1 {
public static void main(String[] args) {
TreeSet<User> t = new TreeSet<User>(new UserComparator()) ; //比较器作为参数
t.add(new User(19, "小明")) ;
t.add(new User(18, "小李")) ;
System.out.println(t);
}
}
public class UserComparator implements Comparator<User> { //实现Comparator接口的比较器类
public int compare(User o1, User o2) {
if(o1.getAge() > o2.getAge()) //降序
return -1 ;
else if(o1.getAge() < o2.getAge())
return 1 ;
else
return 0 ; //返回0代表重复
}
}
public class User {
private int age ;
private String name ;
public User(int age, String name) {
this.age = age ;
this.name = name ;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
}
判断重复的依据是添加的两个元素通过compareTo()进行比较时,若返回0则代表重复
4 Map
专门用来处理 键(key)值(value)映射数据 的集合,根据key实现对value的操作
实现类:HashMap、Hashtable、Proprties
4.1 键值对(key-value):
键值对是一种映射关系,由key可以映射到对应的value
特点:
- key必须唯一
- 一个key只能对应一个value,但一个value可以对应多个key(key不能重复,value可以重复)
- 无法保证元素的顺序和添加顺序一致,从而不能按索引访问元素(可用key访问)
4.2 HashMap
基于哈希算法的集合,以哈希表形式存储
HashMap属于泛型,但泛型参数有两个,分别代表键和值。HashMap<k, v>
4.2.1 常用方法
方法名 | 作用 |
---|---|
put(Object key, Object value) | 添加元素,包括键和值 (输入的key重复时作为修改value) |
get(Object key) | 根据key获取对应的value |
remove(Object key) | 根据key删除对应的键值对 |
containsKey(Object key) | 查看是否包含指定的key |
containsValue(Object value) | 查看是否包含指定的value |
keySet() | 返回包含所有key的Set集合 |
4.2.2 HashSet和HashMap的关系
之前说的HashSet元素不可重复,本质上就是创建了一个HashMap,由于key是唯一的,所以将Set中的元素作为key就实现了元素不重复
4.2.3 遍历元素方法
需要注意的是Map本身没有遍历方法
//通过setKey()方法获得key集合,再通过key进行遍历
Set<Integer> keys = m.keySet() ;
for(Integer key : keys) {
System.out.println(key + " "+ m.get(key));
}
//通过values()方法获得所有的value,再通过value进行遍历
Collection<String> values = m.values() ;
for(String value: values) {
System.out.println(value); //没有办法从值得到键
}
//通过entrySet()获取所有键值对,返回一个Set集合。遍历所有的key和value
Set< Entry<Integer, String> > entris = m.entrySet() ; //Entry是Map里的内部接口,表示键值对
Iterator< Entry<Integer, String> > it = entris.iterator() ;
while(it.hasNext()) {
Entry<Integer, String> entry = it.next() ; //每一个元素的类型都用Entry表示就行
System.out.println(entry.getKey() + " "+ entry.getValue());
}
4.3 Hashtable
4.3.1 Hashtable和HashMap的区别
和HashMap基本用法类似,区别如下
Hashtable | HashMap |
---|---|
同步的,线程安全 | 异步的,线程不安全 |
key和value都不允许为null(编译不报错,运行时报错NullPointer) | key和value都允许为null |
可以使用Iterator和Enumerration遍历 | 不能用Enumerration遍历 |
Hashtable是一种过时的东西,一般使用hashMap就可以了
4.4 Properties
Properties继承自Hashtable,一般只用来存储字符串键值对(键和值都是字符串)
在创建对象是不需要再考虑泛型,默认就是字符串
4.4.1 常用方法
方法 | 作用 |
---|---|
setProperty(String key, String value) | 添加、修改元素 (默认参数类型就是字符串) |
getProperty(String key) | 根据key获取对应元素的value |
propertyNames() | 返回一个枚举类型的key集合 |
4.4.2 读取属性文件
Properties常用来读取属性文件的内容
4.4.2.1 属性文件
- 一般是以.properties为后缀的文件,但实际上不管是什么类型的文件只要符合格式要求就行
- 格式要求:属性名=属性值
- 只支持ISO-8859-1编码,不支持中文,但可以通过IDE自动进行编码转换
读取方法:
使用load(InputStream inStream)方法就可以自动将属性文件中的内容存入到Properties对象中。关键是要读取的属性对象要通过输入流的形式作为参数传递进来。要得到流,就要获得类加载器,要得到类加载器又要得到运行时类。所以参数需要一层层调用方法才能获得输入流文件
Properties p = new Properties() ;
//读取属性文件
p.load(Properties1.class //.class获得运行时类的字节码文件
.getClassLoader() //得到运行时类后获得类加载器,用来加载类路径下的资源(文件)
.getResourceAsStream("data.properties") //将文件作为流获取,符合load()方法参数需要输入流文件
);
System.out.println(p);
System类中含有一个getProperties()方法,返回一个Properties对象,可以获得系统属性
5 Collections类
Collections是一个工具类,提供了集合操作的相关方法,例如排序、查找、求最值等。既然是工具类,则大部分的方法都是静态的,直接拿来用就好了
5.1 常用方法
方法名 | 作用 |
---|---|
addAll(Collection c, Objetc… elements) | 向指定的集合中添加任意个数的元素 |
max(Collection c) | 求出指定集合中的最大值 |
min(Collection c) | 求出指定集合中的最小值 |
sort(Collection c) | 对指定集合中的元素进行排序 |
reverse(Collection c) | 对指定集合中的元素顺序进行反转 |
swap(Collection c, int i, int j) | 将i,j两个位置的元素交换 |
5.2 Collections和Collection的区别
Collection | Collections |
---|---|
接口 | 工具类 |
List、Set实现该接口 | 提供集合的相关操作 |