Java集合框架的基本内容:
本文为博主个人总结,不喜勿喷(^o^)/~
1. Java集合框架概述
- java 集合框架的由来:
其实在Java2/jdk1.2)之前,Java 是没有完整的集合框架的。它只有一些简单的可人自扩展的容器类,比如 Vector,Stack,Hashtable 等。
为什么存在容器类: 容器类(集合类可以存储多个数据,既然数组可以存储多个数据,为什么需要定义容器类?)
数组的弊端
1):长度是不可变的,一旦数组初始化之后,长度是固定的
2) :在N 个地方需要存储多个数据都得专门去编写数组的操作方法如此看来,没有体观DRV原则。代码和功能重复—-封装思想
3):即使每一个人都要使用到数组类,但是不同的人定义的类名和方法名是不同的,实现细节也是参差不齐的,SUN公司就自定义好了容器类,每一个开发者只管调用即可
什么是集合框架:
尽管这些容器类非常好用,但是却不能集中和统一管理。集合框架是为表示和操作集合而规定的一种统一标准的体系结构。任何集合框架都包合三大块内容: 对外的接口、接口的实现和对集合运算的算法底层都对应着某一种数据结构的算去)。
为什么需要集合框架:
1)提供功能的复用 Java.util 包.
2)专注于业务开发而不是数据结构和算法
2. Collection接口
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java 不提供直接继承自 Collection 的类,只提供继承于的子接口(如 List 和 set)的类。
- List集合
- 3.1 List 实现类特点和性能分析
List 接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在 List 中位置,类似于数组的下标)来访问 List 中的元素,而且允许有相同的元素。
三者共同的特点(共同遵循的规范):
1):允许元素重复
2):记录元素的先后; 和添加顺序
List类几种实现类:
Vector类:
底层采用数组结构算法,方法都使用了 synchronized 修饰,线程安全,但是性能相对于 Arraylist 较低。
ArrayList 类:
底层才有数组结构算法,方法没有使用 synchronized 修饰线程不安全性能相对于 vector 较高.
ArrayList 现在机会已经取代了 vector的江湖地位。
为了保证 ArrayList 的线程安全,
List list = Collections synchronizedList(newArraylist(..);
LinkedList 类:底层才有双向链表结构算法,方法没有使用 synchronized 修饰线程不安全.
数组结构算法和双向链表结构算法的性能问题:
数组结构算法: 插入和删除操作速度低,查询和更改较快.
链表结构算法:插入和删除操作速度快,查询和更改较慢
使用的选择:
Vector 类 打 死 不 用 即 使 要 用 选 ArrayList 类.
如果删除和插入操作频繁,应该选择 LinkedList 类
如果查询操作频繁,应该使用 Araylist 类.
在开发中使用 ArrayList 较多
数组结构算法和双向链表结构算法的性能问题:
数组结构算法: 插入和删除操作速度低,查询和更改较快.
链表结构算法:插入和删除操作速度快,查询和更改较慢
- 3.2 Vector类
存储原理:
1.表面上把数据存储到Vector对象中,其实依然把数据储存到 object 数组中
2.数组类型:该组的元素类型为 object
集合中只能存储数据对象,不能存储基本数据类型的值
3.集合中存储的对象,存储的都是对象的引用,而不是对象的本身
Vector 类 的操 作方 法
常用方法:
增加:
boolean add ( Object e ) 将指定元素添加到此向量的末尾等于 addElement 方法。
void add(int index,object element) 在此向量的指定位置插入指定的元素。
boolean addAll (Collection c) :把集合中的元素添加到当前集合对象中.
删除:
object remove( int index) :删除指定索引位 置 的 元 素, 并 返 回 删 之 后 的 元 素.
boolean remove (0bjecto) 删除指定的元素。
boolean removeall(collection c);从此集合中移除包合在指定集合 c 中的所有元素。
boolean retainAll (Collection c ) :在此集合中仅保留包含在指定集合 c 中的元素,求两个集合的交集。
修改:
object set(int index,object element) :修改当前集合中指定索引位置的元素,返回被替换的旧的元素,
查询:
int size() :返回当前集合中存储几个元素。
boolean isEmpty :判断当前集合中元素个数是否为0.
object get( int index)} 查询指定索引位置的元素.
Object[ ] toArray () :把集合对象转换为Object数组.
- 3.3Stack类
栈(stack) 数据结构的一种,存储特点: Last in First out.
stack 类表示后进先出(LIFO) 的对象栈
栈结构 在 生 活中 的体 现:
1)QQ消息A,B,C三个人先后发送消息我们查看的时候发现最顶上的是最新的消息.
2)手枪弹夹的装和发射:
要来实现栈的存储,底层可以用数组来存储,也可以使用链表来存储
java.util.Vect0r
L java.util.Stack
- 3.4 ArrayList类
ArrayList 类是 java 集合框架出现之后用来取代 vector 类的:
二者底层原理都是基于数组的算法,一模一样
区别:
Vector :所有的方法都使用了 synchronized 修饰符.
线程安全但是性能较低,适用于多线程环境
ArrayList :所有的方法都没有使用 Synchronized 修饰符.
线程不安全但是性能较高
即使以后在多线程环境下,我们也不用 Vector 类:
ArrayList list = collections.synchronizedlist(new ArrayList(..) };
- 3.5 LinkedList类
List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:
List list = Collections.synchronizedList(new LinkedList(…));此类的 iterator 和 listIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
4. 迭代集合(Iterator)
集合的迭代操作:
把集合做的元素一个一个的遍历取出来时
迭 代器 对 象:
iterator: 迭代器对象,只能从上往下迭代
(bolean hasNext);判断当前指针后是否有下一个元素
next();获取指针的下一个元素,并且移动指针.
object Listiterator : 是 iterator 接口的子接口,支持双向迭代,从上往下选代,从下往上选代.
Enumeration:古老的迭代器对象现在已经被 iterator 取代了,适用于古老的 Vector 类.
四种遍历格式:
方式一:for 循环
方式二:for each 增强 for 循环
方式三:迭代方法:使用迭代器 iterator()
方式四:使用 for 循环操作迭代器 iterator()
//iterator 遍历
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
public class IteratorDemo {
public static void main(String[] args) {
List<String> li = new ArrayList<>();
li.add("A");
li.add("B");
li.add("C");
System.out.println(li);
//方式一:for循环
for (int index = 0; index < li.size(); index++) {
System.out.println(li.get(index));
}
System.out.println("============================");
//方式二:for each增强for循环
for (String ele : li) {
System.out.println(ele);
}
System.out.println("===========================");
//方式三:迭代方法:使用迭代器iterator()
Iterator<String> it = li.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("===========================");
//方式四:使用for循环操作迭代器iterator()
for (Iterator<String> it2 = li.iterator(); it2.hasNext(); ) {
System.out.println(it2.next());
}
}
}
5. 泛型操作
为什么需要使用泛型:
1):存储任意类型的数据在集合中取出来都是 object 类型的,此时就得强转.
List list = new ArrayList() ;
list.add(1);/ /Integer 类型
Object.ele = list.get(0);
//现在需要调用 Integer 类中的方法
Integer num=(Integer) ele:
system.out.println(num) ;
2) :约束存储到集合中的元素必须是相同的数据类型(相同的数据类型才能做比较比如 treeSet 类,
3):设计一个点(Point) 类,来封装坐标位置,要求坐标位置支持 string 类型. integer 类型/Double 类型.
- 泛型类:
泛型类:直接在类/接口上定义的泛型。
使用泛型:保证前后类型相同
List list = new ArrayList() //该 list 集合中只能存储 String 类型的元素.
因为前后类型相同所以从Java7开始,退出泛型的菱形语法
List list = new Arraylist<>();
泛型不存在继承的关系(错误如下
List list = new Arraylist ();//错误的
从此以后,使用集合都得使用泛型来约束该集合中元素的类型
通过反编译发现: 泛型其实也是语法糖,底层依然没有泛型,而且依然使用强转。
- 泛型方法
泛型方法在方法上声明泛型
情况1):泛型类中的泛型只能适用于非静态方法如果需要绐静态方法设置泛型此时使用泛型方法
情况2): 泛型类中的泛型应该适用于整个类中多个方法,有时候只对某一个方法设置泛型即可.
一般的,把自定义的泛型作为该方法的返回类型才有意义而且此时的泛型必须是由参数设置进来的
如果没有参数来设置泛型的具体类型,此时的方法一般返回设计为 Object 即可.
public static T doWork(T val){
return val ;
}
- 泛型通配符
不知道用哪种类型来接收的时候,此时使用?,
?表示未知,通配符此时只能接收数据,不能往该集合中存储数据
6. Set接口
Set 是 collection 子接口,模拟了数学上的集合的概念。
Set 集合存储特点
1):不允许元素重复
2);不会记录元素的先后添加顺序,
Set 只包含从 collection 继承的方法,不过 set 无法记住添加的顺序,不允许包合重复的元素。当试图添加两个相同元素进 Set 集合,添加操作失败,add(方法返回 false)
Set 判断两个对象是否相等用 equals,而不是使用==。也就是说两个对象 equals 比较返回 true,Set 集合是不会接受这个两个对象的。
- HashSet类
HashSet 是 set 接口最常用的实现类.顾名思义,底层采用了哈希表(散列/hash)算法
其底层其实也是一个数组,存在的意义是提供查询速度,插入速度也比较快,但是适用于少量数据的插入操作.
在Hashset 中如何判断两个对象是否相同问题:
1) :两个对象的 equals 比较相等.返回 true,则说明是相同对象.
2):两个对象的 hashCode 方法返回值相等,
对象的 hashCode 值决定了在哈希表中的存储位置.
二者:缺一不可.
当往 HashSet 集合中添加新的对象的时候先会判断该对象和集合对象中的 hashCode 值:
1) 不等: 直接把该新的对象存储到 hashCode 指定的位置.
2) 相等: 再继续判断新对象和集合对象中的 equals 做比较
1> hashcode 相同 equals 为 true: 则视为是同一个对象则不保存在哈希表中
1>: hashcode 相同,equals 为 false:非常麻烦存储在之前对象后槽为的链表上.
存储在哈希表中的对象都应该覆盖 equas方法和 hashCode方法,并且保证 equals 相等的时候 hashcode也应该相等
LinkedHashSet 类
用来实现既不重复也可以记录顺序的集合TreeSet类
TreeSet: 不保证元素的先后添加顺序,但是会对集合中的元素进行排序操作.
底层才有红黑树算法树结构,比较擅长做范围查询.
TreeSet: 要么采用自然排序 ,要么定制排序
TreeSet 的排序规则:TreeSet 集合底层才有红黑算法,会对存储的元素默认使用自然排序(从小到大)
注意:必须保证 TreeSet 集合中的元素是相同数据类型,否则报错自然排序:
Treeset 调用集合元素的 compareTo 方法来比较元素的大小关系,然后集合元素按照从小到大升序排列).
注意:要求 TreeSet 集合中元素得以实现 java.util.Comparable 接口.
覆盖 public int compareTo(Object O)方法在该方法中编写比较规则
在该方法中,比较当前对象(this)和参数对象 O 做比较.
严格上说比较的是对象中的数据,比如按照对象的年龄
this>O :返回正整数,1
this< O :返回负整数.-1
this ==O : 反回O.
//Treeset 自然排序
import java.util.Set;
import java.util.TreeSet;
class Person implements Comparable<Person>{//继承comparable的比较算法
String name;
int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String toString() {//添加数组字符串打印方法
return "Person [name=" + name + ", age=" + age + "]";
}
public int compareTo(Person others) {//调用方法 compare 比较元素大小
if (this.age>others.age) {
return 1;
}else if (this.age<others.age) {
return -1;
}else {
return 0;
}
}
}
public class TreeSetComparatorDemo {
public static void main(String[] args) {
Set <Person> p = new TreeSet<>();
p.add(new Person("张三",17));
p.add(new Person("李四",7));
p.add(new Person("王五",67));
p.add(new Person("孙武",77));
p.add(new Person("曹操",19));
p.add(new Person("刘备",119));
System.out.println(p);
}
}
- 定制排序:
调用 java.lang.Commparator 对象,并覆盖 public int
compare(object o1,object o2)
package com_.it_04_Set;
//定制排序
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
class Person2 implements Comparator<Person>{
private String name;
private int age;
public Person2(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "Person2 [name=" + name + ", age=" + age + "]";
}
public int compare(Person o1, Person o2) {
if (o1.age>o2.age) {
return 1;
}else if (o1.age<o2.age) {
return -1;
}
return 0;
}
}
public class TreeSetComp {
public static void main(String[] args) {
Set <Person> p = new TreeSet<>();
p.add(new Person("曹操",17));
p.add(new Person("刘备",7));
p.add(new Person("关羽",97));
p.add(new Person("张飞",77));
p.add(new Person("孙权",19));
System.out.println(p);
}
}
- Set实现类性能分析
Hashset :做等值查询效率高,
Treeset :做范围查询效率高
而我们更多的情况,都是做等值查询,在数据库的索引中做范围查询较多,所以数结构主要用于做索引,用来提高查询效率|
7. Map接口
Map不属于collection 接口的实现类,属于java.util包中的实现类
Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。
Map 的常用实现类:
采用哈希表算法,此时 Map 中的 key 不会保证添加的先后顺序 key 也不允许重复
- HashMap类
key 判断重复的标准是:keyl和key2是否equals为true,并且 hashCode 相等.
采用红黑树算法此时 Map 中的 key 会按照自然顺序或定制排序进行排序,key 也不允许重复.
import java.util.Map;
import java.util.TreeMap;
//统计各个字符串出现的次数
public class MapDemo {
public static void main(String[] args) {
String str = "naksdbakbcdabsubabsduiqbxzkjahtpsadxzpqs";
char[] arrs = str.toCharArray();
Map<Character,Integer> map = new TreeMap<Character, Integer>();
for (char ch : arrs) {
if (map.containsKey(ch)) {
Integer val =map.get(ch);
map.put(ch, val+1);
}else {
map.put(ch, 1);
}
}
System.out.println(map);
}
}
//返回map中的key value 值
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
Map<String , Object> map= new HashMap<>();
map.put("1", "sc");
map.put("2", "acx");
map.put("3", "aWs");
map.put("4", "gf");
map.put("5", "fty");
System.out.println(map);
//返回key的值
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key);
}
//返回value的值
Collection<Object> values =map.values();
for ( Object val : values) {
System.out.println(val);
}
//获取entry
Set<Map.Entry<String,Object>> entrySet= map.entrySet() ;
for (Entry<String, Object> entry : entrySet) {
System.out.println(entry);
}
}
}
LinkdeHashMap类
key 判断重复的标准和 HashMap 中的 key 的标准相同TreeMap类
key 判断重复的标准是: compareTo/compare 的返回值是否为0.
采用链表和哈希表算法此时 Map 中的 key 会保证先后添加的顺序,key 不允许重复.
//获取 treemap 中的值
import java.util.Map;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
Map<String,Object> tre = new TreeMap<>();
tre.put("A", "1");
tre.put("I", "2");
tre.put("C", "3");
tre.put("D", "12");
tre.put("E", "13");
tre.put("F", "15");
System.out.println(tre);
System.out.println(tre.get("A"));
}
}
HashTable类
采用哈希表算法是 HashMap 的前身(类似于 Vector 是 ArrayList 的前身).在 Java 的集合框架之前出现
所有的方法都采用 synchronized 修饰符,线程安全,但是性能相对 HashMap 较低properties类
Hashtable 的子类,此时要求 key 和 value 都是 String 类型.用来加载资源文件(properties 文件)
一般的,我们定义 Map key 都 使 用 不 可 变 的类 (String), 把 key 作 为 value 的唯一 名 称Map实现类性能分析
哈希表算法:做等值查询最快
树结构算法:做范围查询最快—->应用到索引上映射关系图
发现在 Map 和 Set 中有很多相类似的实现类名:
Set | Map | 算法 |
---|---|---|
HashSet | Hash Map | 哈希表 |
TreeSet | TreeMap | 红黑树 |
LinkedhashSet | LinkedHashMap | 哈希表/链表等 |
如果集合前缀相同说明底层算法是一样的,现在单独使用 HashSet 和 HashMap 来研究
通过阅读源代码;发现相同算法的 Set 底层用的是相同算法的 Map.
把 Set 的 集 合 对 象 作 为 Map 的 key,再 使 用一 个 Object 常量值为 value.
因此:更符合我们说的在 Map 中,所有的 key 就是一个 set 集合.
8. 集合工具类
集合操作的工具类
1) Arrays 类
2)Collections 类
- Arrays 类:
在 Collection 接口中有一个方法叫 toArray 把集合转换为 object 数组.
把集合转换为数组:
Object[ ] arr = 集合对象.toArray();
数组也可以转换为集合( list )集合:
public staticListasList(T..a) 等价于
public static ListasList(T[a]).
把数组转换为 List 对象:
List List1 = Arrays.asList(“A”,”B”,”C”,”D”) ;
List List2 = Arrays.asList(new Date());
通过 Arrays.aslist 方法得到的 List 对象的长度是周定的,不能增,也不能减
为什么: asList 方法返回的 ArrayList 对象不是 java.utIl.ArrayList 而 是 Arrays 类中的内部谈对象.
//可以自动装箱,把数组看成是 Integer 对象
List List= Arrays.asList(1,2,3,4,5);
//定义了 int 类型的数组,
int[ ] arr2={1,2,3,4,5};
直接把数组当做是对象
List
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/*
* set map List 之间的应用
*/
public class StudentDemo {
public static void main(String[] args) {
//存储一个班的学生
Set<String> list1 = new HashSet<>();
list1.add("曹操");
list1.add("孙权");
list1.add("刘备");
list1.add("张飞");
list1.add("关羽");
Set<String> list2 = new HashSet<>();
list2.add("诸葛亮");
list2.add("孙武");
list2.add("夏侯惇");
list2.add("吕布");
list2.add("小乔");
Set<String> list3 = new HashSet<>();
list3.add("司马懿");
list3.add("大乔");
list3.add("貂蝉");
list3.add("董卓");
list3.add("马岱");
Set<String> list4 = new HashSet<>();
list4.add("赵云");
list4.add("黄忠");
list4.add("孙膑");
list4.add("曹植");
list4.add("曹彰");
//存储多个班的学生
Map<String,Set<String> > map1 =new HashMap<String, Set<String>>();
map1.put("A班", list1);
map1.put("B班", list2);
Map<String,Set<String> > map2 =new HashMap<String, Set<String>>();
map2.put("C班", list3);
map2.put("D班", list4);
//System.out.println(map1);
//存储一个学院的学生
List<Map<String,Set<String> >> li = new ArrayList<Map<String,Set<String>>>();
li.add(map1);
li.add(map2);
System.out.println(li);
}
}