一 集合框架图
- 所有集合类都位于
java.util
包下。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。 - 集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。
- 抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。
- 实现类:8个实现类(实线表示),对接口的具体实现。
- Collection 接口是一组允许重复的对象。
- Set 接口继承 Collection,集合元素不重复。
- List 接口继承 Collection,允许重复,维护元素插入顺序。
- Map接口是键-值对象,与Collection接口没有什么关系。
- Set、List和Map可以看做集合的三大类:
- List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
- Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是集合里元素不允许重复的原因)。
- Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value。
二 Collection接口
Collection接口是单列集合的根接口,其中定义了很多对元素进行操作的方法。Collection接口有两个主要的子接口List和Set(注意Map不是Collection的子接口)。
集合都是支持泛型的,泛型就是可以在编译阶段约束集合只能操作某种数据类型。
Collection接口的API
比较常用的方法,比如方法add()添加一个元素到集合中,addAll()将指定集合中的所有元素添加到集合中,contains()方法检测集合中是否包含指定的元素,toArray()方法返回一个表示集合的数组。
Collection 的遍历
遍历就是一个一个的把容器中的元素访问一遍。
(1)迭代器遍历
迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
获取方法 Iterator<E> iterator() 作用是返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引
Iterator中的常用方法有
boolean hasNext() 作用是询问当前位置是否有元素存在,存在返回true ,不存在返回false;
E next( ) 作用是获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界。
e.g.
(2)增强for循环
增强for循环既可以遍历集合也可以遍历数组
遍历格式:
(3)Lambda表达式遍历集合
得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式
遍历格式:
下面是代码演示
public class Test {
public static void main(String[] args) {
Collection<String> co=new ArrayList(); //泛型
System.out.println("----------添加----------------");
co.add("aaa");
co.add("bbb");
co.add("ccc");
co.add("ddd");
System.out.println(co);
System.out.println("----------清除----------------");
co.clear();
co.remove("ccc");
System.out.println(co);
System.out.println("----------判断----------------");
System.out.println(co.contains("bbb"));
System.out.println(co.isEmpty());
System.out.println("----------长度----------------");
System.out.println(co.size());
System.out.println("----------转数组----------------");
String[] arr=new String[4];
co.toArray(arr);
System.out.println(Arrays.toString(arr));
System.out.println("----------迭代器----------------");
//获取迭代器
Iterator it = co.iterator();
//遍历
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println("----------增强for----------------");
for (Object o : co) {
String u=(String) o;
System.out.println(u);
}
System.out.println("----------forEach----------------");
co.forEach(o -> System.out.println(o));
}
}
Collection接口有两个常用的子接口,下面详细介绍。
1. List接口
List集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。
List接口继承于Collection接口,它可以定义一个允许重复的有序集合。因为List中的元素是有序的,所以我们可以通过使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
List集合特有方法
lList集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
List的实现类的底层原理
- ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢
- LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的
(1)ArrayList
ArrayList是一个动态数组,也是我们最常用的集合。它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量10,该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。
ArrayList集合底层原理
- ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作
- l第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组
ArrayList擅长于随机访问。同时ArrayList是非同步的。
public class Test {
public static void main(String[] args) {
ArrayList arrayList=new ArrayList();
arrayList.add("java");
arrayList.add("php");
arrayList.add("c++");
arrayList.add(3,"cc");
System.out.println(arrayList);
}
}
(2) LinkedList
同样实现List接口的LinkedList与ArrayList不同,ArrayList是一个动态数组,而LinkedList是一个双向链表。所以它除了有ArrayList的基本操作方法外还额外提供了get,remove,insert
方法在LinkedList的首部或尾部。底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
public class Test {
public static void main(String[] args) {
LinkedList linkedList=new LinkedList();
linkedList.add("aa");
linkedList.add("bb");
linkedList.add("cc");
linkedList.addFirst("dd");
linkedList.addLast("ee");
System.out.println(linkedList.getFirst());
System.out.println(linkedList.getLast());
System.out.println(linkedList.removeFirst());
System.out.println(linkedList.removeLast());
System.out.println(linkedList);
}
}
2 set 接口
Set是一种不包含重复的元素的Collection,无序,不重复,无索引,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
Set集合实现类特点
- HashSet : 无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:排序、不重复、无索引
Set集合的功能上基本上与Collection的API一致。
(1)HashSet
底层原理
- HashSet集合底层采取哈希表存储的数据
- 哈希表是一种对于增删改查数据性能都较好的结构
HashSet去重复原理解析
结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法
(2)LinkedHashSet
特点:有序、不重复、无索引
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
(3)TreeSet
TreeSet集合概述和特点
- 不重复、无索引、可排序
- 可排序:按照元素的大小默认升序(有小到大)排序。
- TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
- 注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
TreeSet支持两种排序方式,自然排序和自定义排序,其中自然排序为默认的排序方式
三 Map接口
Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以相同。
Map集合体系特点
- Map集合的特点都是由键决定的。
- Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)。
- Map集合后面重复的键对应的值会覆盖前面重复键的值。
- Map集合的键值对都可以为null。
Map集合实现类特点
- HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)
- LinkedHashMap:元素按照键是有序,不重复,无索引,值不做要求。
- TreeMap:元素按照建是排序,不重复,无索引的,值不做要求。
Map集合的API
Map集合的遍历方式
- 方式一:键找值的方式遍历:先获取Map集合全部的键,再根据遍历键找值。
- 方式二:键值对的方式遍历,把“键值对“看成一个整体,难度较大。
- 方式三:JDK 1.8开始之后的新技术:Lambda表达式。
public class MapTest {
public static void main(String[] args) {
//创建map集合
Map<String,String> map=new HashMap<>();
System.out.println("-----------添加-----------------");
map.put("张亚东","尚志");
map.put("马影","牡丹江");
map.put("林瑞鹏","齐齐哈尔");
System.out.println(map);
System.out.println("-----------删除-----------------");
map.remove("尚志");
System.out.println(map);
System.out.println("-----------删除所有-----------------");
//map.clear();
System.out.println(map);
System.out.println("-----------判断-----------------");
System.out.println(map.containsKey("马影"));
System.out.println(map.containsValue("牡丹江"));
System.out.println(map.isEmpty());
System.out.println(map.size());
System.out.println("键找值的方式遍历:先获取Map集合全部的键,再根据遍历键找值");
Set<String> strings = map.keySet();
for (String string : strings) {
System.out.println(string+"---"+map.get(string));
}
System.out.println("-----------------键值对遍历------------------");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey()+"-----"+entry.getValue());
}
System.out.println("----------Lambda-----------------");
map.forEach((k,v)-> System.out.println(k+"---"+v));
}
}
1 HashMap
特点和底层原理
- HashMap是Map里面的一个实现类。特点都是由键决定的:无序、不重复、无索引
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
- HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每个元素包含两个值而已。
- 由键决定:无序、不重复、无索引。HashMap底层是哈希表结构的。
- 依赖hashCode方法和equals方法保证键的唯一。
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法。
- 基于哈希表。增删改查的性能都较好。
2 LinkedHashMap
特点和底层原理
- 由键决定:有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
3 TreeMap
特点和底层原理
- 由键决定特性:不重复、无索引、可排序
- 可排序:按照键数据的大小默认升序(有小到大)排序。只能对键排序。
- 注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键按照指定的规则进行排序
- TreeMap跟TreeSet一样底层原理是一样的。