Java集合及其工具类
一、常用的两种遍历集合的方法
-
迭代器的实现
Iterator<String> it =coll.iterator(); while(it.hasNext()){ String line =it.next(); System.out.println(line); }
实现原理: 首先指针会指向集合的-1索引,判断hasNext之后就会输出next直到最后一位。
-
增强for循环
底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写
所有的单列集合都可以使用增强性for循环
作用: 用来遍历数组或者集合格式:
for(集合/数组的数据类型 变量名:集合/数组名){
System.out.println(变量名);
}
二、泛型
-
泛型: 泛型是一种未知的数据类型,当我们不知道使用什么数据类型的时候可以使用泛型,泛型也可以看作一个变量,用来接受数据类型
E e:Element 元素
T t: Type 类型
例:ArrayList源码中,ArrayList不知道将要保存什么数据类型,所以使用泛型E
表示未知的数据类型
##当创建集合对象的时候就会确定泛型的数据类型
例:List < String > list =new Arraylist<>();
会把数据类型作为参数传递,把String的值传给EList< Student> list =new ArrayList<>();
此时,会把Student的值作为参数传递给E -
使用泛型的好处:
创建集合对象,不使用泛型
好处:集合不使用泛型,默认使用Object类型,可以存储任意类型的数据
弊端:不安全,会引发数据异常:类型转换异常 java.lang.ClassCastException创建集合对象,使用泛型
好处:避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
把运行期的异常(运行代码时候的异常)提升到了编译期的异常(写代码时期) -
定义含有泛型的方法: 泛型定义在修饰符和返回值之间
格式:修饰符 <泛型> 返回值类型 方法名(参数列表(可以使用泛型)){
方法体;
}含有泛型的方法,在调用方法的时候确定泛型的类型
传递什么类型的参数,泛型就是什么类型
public static void method(E e){
System.out.println(e);
}
//这里,如果传递的是一个String类型的数据,则输出的即为字符串类型 -
泛型的通配符: ?代表任意的数据类型
使用方式:不能创建对象使用,只能作为方法的参数使用
例如:创建一个方法,能遍历所有的ArrayList集合
这是时候我们不知道这是什么数据类型,所以使用泛型的通配符?来接收数据
注意:泛型是没有继承概念的public static void printArray(ArrayList<?> list){ //使用迭代器遍历集合 Iterator<?> it=list.iterator(); while(it.hasNext()){ it.next()方法取出的是Object类型的数据,可以接受任何数据类型 Object obj =it.next(); System.out.println(obj); } }
泛型的上限限定和泛型的下限限定
上限限定:?extends E 代表使用的泛型只能是E类型的本身或子类
下限限定:?super E 代表使用的泛型只能是E类型的本身或父类
三、List集合
-
List 集合:list接口继承collection接口。
list接口的特点:
1、有序的集合,存储元素的顺序是一致的
2、有索引,包含了一些带索引的方法
3、允许存储重复的元素
list接口中带索引的方法:
1、public void add(int index,E element)
2、public E get(int index)
3、public E remove(int index)
4、public E set(int index,E element)
注意:操作索引的时候,一定要注意防止索引越界
打印list集合的三种方法:
普通的for循环 增强型for循环 Iterator-
ArrayList集合:
集合数据的存储结构是数组结构,元素的查找快、增删慢,在日常的开发中使用最多的是数据查询,遍历数据 -
LinkedList集合:
1、底层是一个链表结构:查询慢,增删快
2、里面包含了大量操作首位元素的方法
注意:使用LinkedList集合特有的方法,不能使用多态插入方法: addFirst() 插入开头
addLast() 插入结尾
push() 将元素推入此列表表示的堆栈返回: getFirt() 返回列表的第一个元素
getLast() 返回列表的最后一个元素移除: removeFirst()移除列表的第一个并返回列表的第一个元素
removeLast()移除列表的最后一个并返回最后一个元素
pop()从此列表的堆栈处弹出一个元素判断是否为空: boolean isEmpty()
-
Vector: 底层与数组一样,底层使用索引进行存储。Vector是同步的,代表是单线程,索引存储速度慢
-
四、Set集合:
特点:1、不允许存储重复的元素
2、没有索引,没有带索引的方法,也不能使用普通的for循环遍历
-
HashSet集合implements Set接口
HashSet特点:
1、不允许存储重复的元素
2、没有索引,没有带索引的方法,也不能使用普通的for循环遍历
3、是一个无序的集合,存储元素和取出元素的顺序可能不一致
4、底层是一个哈希表结构(查询速度非常快) -
HashSet集合不能使用普通的for循环遍历,可以使用增强型for循环和迭代器
-
哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到 的地址不是数据实际存储的物理地址)
在Object类中有一个方法,可以获取对象的哈希值
int hashCode():返回该对象的哈希码值
hashCode 的源码:
public native int hashCode();
代表该方法调用的是本地操作系统的方法
HashSet集合存储数据的结构(哈希表)
jdk1.8版本之前:哈希表=数组+链表
jdk1.8/版本之后:哈希表=数组+链表
哈希表=数组+红黑树(提高查询速度)
哈希表的特点:速度快
存储数据到集合中,先计算元素的哈希值,相同哈希值的元素分为一组,链表/红黑树结构把相同哈希 值的元素连接在一起
如果链表的长度超过了9位,那么就会把链表转换为红黑树(提高查询速度)
note:两个元素不同,但是哈希值相同,被称为哈希冲突
Set集合存储元素不重复的原理:Set集合在调用add方法的时候,会调用元素的hasCode方法和 equals方法,判断元素是否重复
HashSet集合在存储自定义类型的元素的时候,必须要重写HashCode方法和equals方法,这样才 能保证不会存储相同的元素 -
LinkedHashSet:底层是一个哈希表(数组+链表/红黑树)+链表(多了一条链表,记录元素的存储位置, 保证元素的有序)
-
可变参数:是JDK1.5之后出现的新特性
使用前提:当方法的参数列表数据类型已经确定,但是参数的个数不确定,此时可以使用可变参数
使用格式:定义方法的时候使用:
修饰符 返回值类型 方法名(数据类型…变量名){}
可变参数的原理:可变参数的底层就是一个数组,根据传入参数个数的不同,创建不同长度的数组
可变参数的注意事项: 1、一个方法的参数列表,只能有一个可变参数
2、如果方法的参数有多个,那么可变参数一定要写在参数列表的末尾
五、Collections集合类:
- 1、addAll(集合变量名,添加的元素……)
shuffle(集合变量名)
List<String> list =new ArrayList<>();
//往list集合中添加元素
Collections.addAll(list, "a","b","c","d","e","f","g","h");
System.out.println(list);
//把list集合打乱顺序
Collections.shuffle(list);
System.out.println(list);
- 2、 sort(集合变量名)方法
public static void main(String[] args) {
ArrayList<Integer> list =new ArrayList<>();
Collections.addAll(list, 1,2,3,4,5,8,7,6);
System.out.println(list); //[1, 2, 3, 4, 5, 8, 7, 6]
Collections.sort(list);
System.out.println(list); //[1, 2, 3, 4, 5, 6, 7, 8]
List<Person> p=new ArrayList<>();
p.add(new Person("张三", 18));
p.add(new Person("李四", 20));
p.add(new Person("王五", 15));
System.out.println(p); //[Person [name=张三, age=18], Person [name=李四, age=20], Person [name=王五, age=15]]
//在person这个类中,重写compareto方法,this.getAge()-o.getAge()为升序,反之为降序
//注意:在类名之后要implements Compareble接口<Person>,因为是将Person作比较,所以要将泛型写成Person
Collections.sort(p);
System.out.println(p); //[Person [name=王五, age=15], Person [name=张三, age=18], Person [name=李四, age=20]]
}
- 3、sort(集合变量名,new Comparator< T>(){
@Override
public int compare(T t1,T t2){
return 0;
//这里用t1-t2代表0,表示升序。使用o2-o1则表示降序
}
})
这个和2中的方法代码区别在参数不同,3中的排序方式可以自己改变(升序、降序)
六、Map集合
* Map集合: java.util.Map<k,v>集合
* Map集合的特点:
1、Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
2、Map集合的元素,key和value的数据类型可以相同,也可以不同
3、Map集合中的元素,key是不允许重复的,value是可以重复的
4、Map集合中的元素,key和value的值是一一对应的
java.util.HashMap<k,v>集合 implements Map<k,v>接口
* HashMap集合的特点:
1、HashMap集合的底层是哈希表,查询的速度特别快
JDK1.8之前:数组+单项链表
JDK1.8之后:数组+单项链表/红黑树(链表的长度超过8,就会使用红黑树),提高查询速度
2、HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
* LinkedHashMap的特点:
1、LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
2、LinkedHashMap集合是一个有序的集合,存取元素的顺序是一致的
-
Map集合的第一种遍历方式:通过键找值的方式
Map集合中的方法:
Set keySet返回映射中包含的键的Set视图
实现步骤:
1、使用Map集合中的方法KeySet(),把Map集合所有的key取出来,存储到一个Set集合中
2、遍历Set集合,获取Map集合中的每一个Key
3、通过Map集合中的方法get(key),通过key找到value源代码如图所示:
public static void Demo02() {
Map<String,Integer> map=new HashMap<>();
map.put("张三", 18);
map.put("李四", 15);
map.put("王五", 20);
//使用Map集合的keyset(),把集合当中的所有的key取出来,存储到一个set集合中
Set<String > set=map.keySet();
//使用迭代器遍历set集合的每一个key
Iterator<String > it =set.iterator();
while(it.hasNext()) {
String key=it.next();
Integer value =map.get(key);
System.out.println(key+"="+value);
}
// 以下为增强型for循环遍历集合
for(String key:map.keySet()) {
Integer value=map.get(key);
System.out.println(key+"="+value);
}
}
- Map集合遍历的第二种方式:使用Entry对象遍历
Map集合中的方法:Set<Map.Entry<K,V>> entrySet() 返回映射中包含的映射关系的Set视图
实现步骤:
1、使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
2、遍历Set集合,获取每一个Entry对象
3、使用Entry对象中的方法getKey()和getValue()获取键与值
public static void Demo03() {
Map<String,Integer> map=new HashMap<>();
map.put("张三", 18);
map.put("李四", 20);
map.put("王五", 15);
//将Map集合中的方法entrySet(),把Map集合中的多个Entry对象取出来,存储到一个set集合当中
Set<Map.Entry<String, Integer>> set =map.entrySet();
//迭代器输出
Iterator<Entry<String, Integer>> it =set.iterator();
while(it.hasNext()) {
//使用Entry对象的getKey和getValue获取键与值
Map.Entry<String, Integer> entry=it.next();
String key=entry.getKey();
int value=entry.getValue();
//使用迭代器遍历map集合中的元素
System.out.println(key+"="+value);
}
System.out.println("============以下为增强型for循环================");
for(Map.Entry<String, Integer> entry:set) {
String key=entry.getKey();
int value=entry.getValue();
System.out.println(key+"="+value);
}
}
-
hashMap集合存储自定义类型的键值:
Map集合保证key值是唯一的:作为key的元素,必须重写hasCode和equals方法,以保证键值的唯一代码如下所示:
public static void Demo04() {
Map<String,Person> map=new HashMap<>();
map.put("北京", new Person("张三", 18));
map.put("上海", new Person("李四", 15));
map.put("深圳", new Person("王五", 22));
map.put("北京", new Person("赵六", 28));
//使用keyset()拿到key
Set<String > set=map.keySet();
//用迭代器遍历集合
Iterator<String> it=set.iterator();
while(it.hasNext()) {
String key=it.next();
Person value=map.get(key);
System.out.println(key+"="+value);
}
}
public static void Demo05() {
Map<Person,String> map=new HashMap<>();
map.put( new Person("张三", 18), "北京");
map.put( new Person("李四", 15),"上海");
map.put( new Person("王五", 22),"深圳");
map.put( new Person("张三", 18),"北京");
//用Entry拿到key-value键值对
Set<Map.Entry<Person, String>> set =map.entrySet();
//用增强型for循环遍历集合
for(Map.Entry<Person, String> entry:set) {
Person key=entry.getKey();
String value =entry.getValue();
System.out.println(key+"="+value);
}
}
-
LinkedhashMap集合:
底层是哈希表+链表结构
note:这层链表结构使集合的存取变得有序HashMap:无序且不可重复
LinkedHashMap:有序且不可重复 -
Hashtable集合:
java.util.Hashtable<k,v>集合 implements Map<k,v>接口
Hashtabel:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢
HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快HashMap集合(之前学的所有集合):可以存储null值,null键
Hashtable集合:不能存储null值,null键Hashtable和vector集合一样,在jdk1.2版本之后被被先进的集合(HashMap,ArrayList)取代了
Hashtable 的子类propertices依然活跃在历史舞台
propertices集合是唯一一个和IO流相结合的集合