1、java集合框架所提供的接口和类全部在java.util包中;
2、集合框架是为表示和操作集合而规定的一种统一的标准体系结构;
3、集合框架包含三大块内容:对外接口,接口实现,对集合运算的算法;
4、集合框架共有三大类接口:List,Set,Map,其中List和Set是Collection的子接口;
5、Collection存储一组允许重复、无序的对象;
Set接口继承Collection接口,存储一组不允许重复、无序的对象;
List接口继承Collection接口,存储一组允许重复,有序的对象;
Map接口存储一组成对的键-值对象,提供key-value的映射,其中,key不允许重复,value允许重复,都不要求有序;
Iterator接口是负责定义访问和遍历元素的接口;
6、实现List接口的常用类有 ArrayList 和 LinkedList;
ArrayList对数组进行了封装,实现了长度可变的数组,它的优点在于遍历和随机访问元素的效率比较高;
LinkedList采用链表存储方式,优点在于插入、删除时效率比较高;
7、数组线性表类ArrayList包含的方法:
boolean add(Object o):在列表末尾顺序添加元素,起始索引位置从0开始;
void add(int index,Object o):在指定索引位置添加元素,原索引位置的元素及其后面的元素依次后移;
Object set(int index,Object o):将指定索引位置的对象修改为o;
int size():返回列表中的元素个数;
Object get(int index):返回制定位置处的元素;
boolean contains(Object o):判断列表中是否存在指定元素;
boolean remove(Object o):从列表中删除元素;
Object remove(int index):从列表中删除制定位置的元素,其索引位置从0开始;
package 集合框架;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
/**
* 创建宠物对象
*/
Pet pet1 = new Dog("欢欢", "拉布拉多");
Pet pet2 = new Dog("大大", "萨摩耶");
Pet pet3 = new Dog("跳跳", "柯基");
Pet pet4 = new Dog("汤圆", "京巴");
// 创建集合保存信息
List<Pet> list = new ArrayList<Pet>();
// 将对象添加到集合中
list.add(pet1);
list.add(pet2);
list.add(pet3);
list.add(0, pet4);
// 输出删除之前集合中原始存在的元素数量,并且遍历它们
System.out.println("删除之前共有:" + list.size() + "条狗狗!");
System.out.println("分别是:");
for (int i = 0; i < list.size(); i++) {
Pet pet = list.get(i);
System.out.println(pet.getName() + "," + pet.getStrain());
}
// 删除集合中下标为0的对象和pet2对象
list.remove(0);
list.remove(pet2);
// 显示删除之后集合中剩余的元素数量,并且遍历它们
System.out.println("删除之后还有:" + list.size() + "条狗狗!");
System.out.println("分别是:");
for (int i = 0; i < list.size(); i++) {
Pet pet = list.get(i);
System.out.println(pet.getName() + "," + pet.getStrain());
}
// 查询集合中是否包含pet4对象的信息
if (list.contains(pet4)) {
System.out.println("集合中包含pet1的信息!");
} else {
System.out.println("集合中不包含pet1的信息!");
}
/**
* 输出信息:
* 删除之前共有:4条狗狗!
* 分别是:
* 汤圆,京巴
* 欢欢,拉布拉多
* 大大,萨摩耶
* 跳跳,柯基
* 删除之后还有:2条狗狗!
* 分别是:
* 欢欢,拉布拉多
* 跳跳,柯基
* 集合中不包含pet1的信息!
*/
}
}
8、链表类LinkedList包含的方法
LinkedList除了第7点包含的方法之外,还包含一些特殊的方法,能够更方便的执行添加、查询、删除元素操作:
void addFirst(Object o)
void addLast(Object o)
解释:在列表首部或尾部添加元素;
Object getFirst()
Object getLast()
解释:返回列表中的第一个或最后一个元素;
Object removeFirst()
Object removeLast()
解释:删除并返回列表中第一个或最后一个元素;
package 集合框架;
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
/**
* 创建宠物对象
*/
Pet pet1 = new Dog("欢欢", "拉布拉多");
Pet pet2 = new Dog("大大", "萨摩耶");
Pet pet3 = new Dog("跳跳", "柯基");
Pet pet4 = new Dog("汤圆", "京巴");
Pet pet5 = new Dog("小黑", "金毛");
// 创建集合保存信息,如果是用List接口创建的LinkedList集合,那么当调用LinkedList中特有的方法时,需要强制类型转换
LinkedList<Pet> list = new LinkedList<Pet>();
// 将对象添加到集合中
list.add(pet1);
list.add(pet2);
list.add(pet3);
list.addFirst(pet5);
list.addLast(pet4);
// 输出删除之前集合中原始存在的元素数量,并且遍历它们
System.out.println("删除之前共有:" + list.size() + "条狗狗!");
System.out.println("分别是:");
for (int i = 0; i < list.size(); i++) {
Pet pet = list.get(i);
System.out.println(pet.getName() + "," + pet.getStrain());
}
// 删除集合中下标为0的对象,pet2对象之后,再删除剩余对象中的第一个和最后一个对象
list.remove(0);
list.remove(pet2);
list.removeFirst();
list.removeLast();
// 显示删除之后集合中剩余的元素数量,并且遍历它们
System.out.println("删除之后还有:" + list.size() + "条狗狗!");
System.out.println("分别是:");
for (int i = 0; i < list.size(); i++) {
Pet pet = list.get(i);
System.out.println(pet.getName() + "," + pet.getStrain());
}
// 查询集合中是否包含pet4对象的信息
if (list.contains(pet4)) {
System.out.println("集合中包含pet4的信息!");
} else {
System.out.println("集合中不包含pet4的信息!");
}
/**
* 删除之前共有:5条狗狗!
* 分别是:
* 小黑,金毛
* 欢欢,拉布拉多
* 大大,萨摩耶
* 跳跳,柯基
* 汤圆,京巴
* 删除之后还有:1条狗狗!
* 分别是:
* 跳跳,柯基
* 集合中不包含pet4的信息!
*/
}
}
9、Set接口是Collection的另一个常用的子接口,Set接口常用的实现类有HashSet,因为存储的对象是无序且唯一的,所以所有的需要索引的方法,Set接口都没有;
HashSet类的常用方法:
boolean add(Object o):如果此Set中尚未包含指定元素,则添加指定元素;
void clear():从此Set中移除所有元素;
int size():返回此Set中元素的数量;
boolean isEmpty():如果此Set中不包含任何元素,则返回true;
boolean contains(Object o):如果此Set中包含指定元素,则返回true;
boolean remove(Object o):如果指定元素存在于此Set中,则将其移除;
package 集合框架;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
/**
* 创建宠物对象
*/
Pet pet1 = new Dog("欢欢", "拉布拉多");
Pet pet2 = new Dog("大大", "萨摩耶");
Pet pet3 = new Dog("跳跳", "柯基");
Pet pet4 = new Dog("汤圆", "京巴");
Set<Pet> set = new HashSet<Pet>();
set.add(pet1);
set.add(pet2);
set.add(pet3);
set.add(pet4);
System.out.println("现在集合中存在的狗狗数量为:" + set.size());
set.remove(pet1);
// 由于HashSet存储的对象是无序的,所以也就不存在索引,因此不能用普通for循环遍历
for (Object o : set) {
System.out.println(o);
}
if (set.contains(pet2)) {
System.out.println("存在pet2的信息!");
} else {
System.out.println("不存在pet2的信息!");
}
/**
* 现在集合中存在的狗狗数量为:4
* 宠物名字是:汤圆,品种是:京巴
* 宠物名字是:跳跳,品种是:柯基
* 宠物名字是:大大,品种是:萨摩耶
* 存在pet2的信息!
*/
}
}
10、TreeSet是一个不同步的非线程安全的二叉树;
TreeSet存储数据时,考虑以下两种情况:
TreeSet存储基本数据类型的数据时,排序方式是按照基本数据类型的既定顺序进行排序的,会在内部调用compareTo方法对数据进行排序和检查其唯一性;
TreeSet存储自定义的数据类型时,需要让该类或该类的直接父类实现Comparable接口,之后重写其中的compareTo方法,在重写的compareTo方法中需要重写排序方式和检查其唯一性的代码,例如:
package 集合框架;
import java.util.Set;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
Pet pet1 = new Dog("小赵", "柯基");
Pet pet2 = new Dog("小青", "萨摩");
Pet pet3 = new Dog("小白", "金毛");
Pet pet4 = new Dog("小金", "京巴");
Set<Pet> set = new TreeSet<Pet>();
set.add(pet1);
set.add(pet2);
set.add(pet3);
set.add(pet4);
for (Pet pet : set) {
System.out.println(pet);
}
/**
* Console输出:
* 宠物名字是:小白,品种是:京巴
* 宠物名字是:小金,品种是:金毛
* 宠物名字是:小青,品种是:萨摩
* 宠物名字是:小赵,品种是:柯基
*/
}
}
基本数据类型的值的排序是比较简单的,此处不再赘述,下面的代码给出的是针对汉字进行字典顺序排序的实现:
@Override
public int compareTo(Pet pet) {
// Collator 类是用来执行区分语言环境这里使用CHINA
Comparator<Object> cmp = Collator.getInstance(java.util.Locale.CHINA);
// 当宠物名字和品种都相同时,说明对象是重复的,此时直接返回0
if (this.getName().equals(pet.getName()) && this.getStrain().equals(pet.getStrain())) {
return 0;
}
// 如果名字相同,品种不同,则按照名字排序,反之,按照品种排序
if (this.getName().equals(pet.getName()) && !(this.getStrain().equals(pet.getStrain()))) {
String strain[] = new String[] { this.getStrain(), pet.getStrain() };
// jdk自带对数组进行排序
Arrays.sort(strain, cmp);
if (strain[0].equals(this.getStrain())) {
return -1;
} else {
return 1;
}
} else {
String name[] = new String[] { this.getName(), pet.getName() };
Arrays.sort(name, cmp);
if (name[0].equals(this.getName())) {
return -1;
} else {
return 1;
}
}
}
11、Map接口——HashMap集合类
(1)HashMap基本介绍
HashMap是java中使用最为频繁的map类型,其读写效率较高,但是因为其是非同步的,即读写等操作都是没有锁保护的,所以在多线程场景下是不安全的,容易出现数据不一致的问题;在单线程场景下非常推荐使用;
HashMap其实就是一个散列表,它是通过拉链法来解决哈希冲突的,影响HashMap性能的主要有两个参数,初始容量和加载因子,容量是可以存储的条目数量,加载因子是哈希表在存满之前,可以达到多满的一种尺度,如果哈希表中的条目数超过了当前容量与加载因子的乘积,则要对哈希表进行rehash操作,也就是重建内部数据结构,来扩大哈希表的容量;
哈希表中的键值对都是存储在Entry数组中的;
(2)HashMap包含四个构造函数:
使用默认容量16,默认加载因子0.75f创建的HashMap集合;
指定容量和加载因子创建的集合,如果指定容量小于0,指定加载因子小于0或为空,都会抛出IllegalArgumentException(不合法参数)异常,如果指定容量大于默认最大允许的容量1*2^30,则用该最大容量替换指定容量;
仅指定最大容量的构造函数,加载因子使用默认的0.75f;
以指定Map集合最为参数的构造函数,将会创建一个新的Map集合,将参数集合中的元素,逐个添加到新创建的集合中,创建新的Map集合的时候,会调用指定容量和加载因子的构造函数,指定的容量为:参数集合的容量或者默认容量16,两个容量相比,哪个大就用哪个容量,指定的加载因子用的是默认的0.75f,计算参数集合容量的方式为:m.size()/默认加载因子,由于需要将结果强制转换为int类型的整型值,所以有可能损失小数部分的精度,因此,需要在强制转换之后的结果基础上+1;
(3)HashMap实现的接口:
Cloneable接口,即实现了clone方法,作用是克隆一个HashMap对象并返回;
Serializable接口,分别实现了串行读取和写入功能,readObject()和writeObject()方法的作用是:将HashMap的总的容量,实际容量以及所有的Entry依次读取或者写入到输出流中;
(4)HashMap的遍历方式:
package 集合框架;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
@SuppressWarnings("unchecked")
//用Entry来遍历是效率最高的
private static void getKey_Value(Map<String, String> map) {
Iterator<?> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry<String, String>) iterator.next();
System.out.println("key是:" + entry.getKey() + ",value是" + entry.getValue());
}
System.out.println("--------------------------------------------------------------");
}
private static void getKey(Map<String, String> map) {
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
System.out.println("键分别是:" + iterator.next());
}
System.out.println("--------------------------------------------------------------");
}
private static void getValue(Map<String, String> map) {
Iterator<String> iterator = map.values().iterator();
while (iterator.hasNext()) {
System.out.println("值分别是:" + iterator.next());
}
System.out.println("--------------------------------------------------------------");
}
public static void main(String[] args) {
// 创建集合
Map<String, String> map = new HashMap<String, String>();
// 存储数据
map.put("CN", "中华人民共和国");
map.put("RU", "俄罗斯联邦");
map.put("FR", "法兰西共和国");
map.put("US", "美利坚合众国");
System.out.println("CN对应的国家是:" + map.get("CN"));
System.out.println("目前共存储了" + map.size() + "条信息!");
// 遍历键值对,单独遍历键,单独遍历值
getKey_Value(map);
getKey(map);
getValue(map);
// 以键判断是否存在指定的键值对,如果存在,则输出键对应值后删除该键值对映射
if (map.containsKey("CN")) {
System.out.println("存在指定的key,对应的国家是:" + map.get("CN"));
map.remove("CN");
}
// 判断是否删除成功
if (!map.containsKey("CN")) {
System.out.println("删除成功!");
}
// 清空所有的键值对映射
map.clear();
// 判断是否清空成功
if (map.isEmpty()) {
System.out.println("已经删除所有键值对映射!");
}
/**
* 输出:
* CN对应的国家是:中华人民共和国
* 目前共存储了4条信息!
* key是:RU,value是俄罗斯联邦
* key是:CN,value是中华人民共和国
* key是:FR,value是法兰西共和国
* key是:US,value是美利坚合众国
* --------------------------------------------------------------
* 键分别是:RU
* 键分别是:CN
* 键分别是:FR
* 键分别是:US
* --------------------------------------------------------------
* 值分别是:俄罗斯联邦
* 值分别是:中华人民共和国
* 值分别是:法兰西共和国
* 值分别是:美利坚合众国
* --------------------------------------------------------------
* 存在指定的key,对应的国家是:中华人民共和国
* 删除成功!
* 已经删除所有键值对映射!
*/
}
}
12、使用泛型集合在创建集合对象时指定集合中元素的类型,从集合中取出元素时无须进行类型强制转换,但如果把非指定类型对象放入集合,会出现编译错误;
13、对于集合有用的方法
用数组来创建集合:
Integer arr[]= {1,2,3,4,5,6};
//asList()方法中存放的是一个对象
List<Integer> list = new ArrayList<>(Arrays.asList(arr));
用集合来创建数组:
Integer arr2[]=new Integer[list.size()];
//将list集合中的元素复制进入数组arr2中
list.toArray(arr2);
如果集合中的元素是可以用来比较的,如整数,双精度浮点数或者字符串(字母型字符串),则可以调用java.util.Collections中的静态方法sort进行排序:
String arr[]= {"pear","manggo","hello","zoo","pig","apple"};
List<String> list=new ArrayList<>(Arrays.asList(arr));
Collections.sort(list);
System.out.println(list);
/**
* 输出:
* [apple, hello, manggo, pear, pig, zoo]
*/
如果是汉字类型的中文字符串,排序方式参考前面重写的compareTo()方法;
还可以用Collections类中的max和min方法,返回集合中的最大值和最小值:
String arr[]= {"pear","manggo","hello","zoo","pig","apple"};
List<String> list=new ArrayList<>(Arrays.asList(arr));
Collections.sort(list);
System.out.println(list);
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
/**
* 输出:
* [apple, hello, manggo, pear, pig, zoo]
* zoo
* apple
*/
还可以用Collections类中的shuffle()方法,来随机打乱集合中的元素顺序:
String arr[]= {"pear","manggo","hello","zoo","pig","apple"};
List<String> list=new ArrayList<>(Arrays.asList(arr));
Collections.shuffle(list);
System.out.println(list);
/**
* 输出:
* [zoo, manggo, pig, apple, hello, pear]
*/
14、使用集合实现模拟栈
栈是限定仅在表的一端进行插入和删除运算的线性表:
(1)通常称插入、删除的这一端为栈顶,另一端为栈底;
(2)当表中没有元素时称为空栈;
(3)栈是先进后出(First in Last out)的线性表,简称FILO表,也称为后进先出(LIFO);
(4)从栈中删除元素称为退栈;
package 自定义栈类;
import java.util.ArrayList;
/**
* @author 叶孤城
* isEmpty:判断是否为空栈;
* size:返回该栈目前存储的元素数量;
* peek:获取并返回栈顶元素;
* pop:删除栈顶元素并返回它;
* push:添加一个新的元素
* toString:以字符串格式输出栈信息;
*/
public class MyStrack {
private ArrayList<Object> list=new ArrayList<Object>();
public boolean isEmpty() {
return list.isEmpty();
}
public int size() {
return list.size();
}
public Object peek() {
return list.get(list.size()-1);
}
public Object pop() {
Object o = list.get(list.size()-1);
list.remove(o);
return o;
}
public void push(Object o) {
list.add(o);
}
@Override
public String toString() {
return "Strack"+list.toString();
}
}
下面是测试类:
package 自定义栈类;
public class Test {
public static void main(String[] args) {
MyStrack strack = new MyStrack();
System.out.println("是空栈吗?" + strack.isEmpty());
for (int i = 0; i < 5; i++) {
int a = (int) (Math.random() * 32) + 23;
strack.push(a + "");
}
System.out.println("输出栈信息:" + strack.toString());
System.out.println("是空栈吗?" + strack.isEmpty());
System.out.println("栈中有" + strack.size() + "个元素");
System.out.println("栈顶元素是:" + strack.pop());
System.out.println("现在的栈顶元素是:" + strack.peek());
System.out.println("输出栈信息:" + strack.toString());
/**
* 输出: 是空栈吗?true
* 输出栈信息:Strack[42, 39, 33, 45, 50]
* 是空栈吗?false
* 栈中有5个元素
* 栈顶元素是:50
* 现在的栈顶元素是:45
* 输出栈信息:Strack[42, 39, 33, 45]
*/
}
}
在这个例子中,ArrayList和MyStrack是一种组合关系,它定义了一个全新的类,而无须集成ArrayList中不必要和不恰当的方法;