集合
容器
容器的作用和概括
为什么使用容器
数组回顾
数组是一种容器,可以在其中放置一个或一组数据。
优势:是一种简单的线性序列,可以快速的访问数组元素,效率高。
劣势:不灵活,容量事先定义好,不能随着需求的变化而扩容。
数组远远不能满足业务需求。我们需要一种 灵活的 , 容量可以随时扩充的 容器来装载我们的对象。这就是我们今天要学习的容器类,或者叫集合框架;
容器中的接口层次结构
![](https://img-blog.csdnimg.cn/img_convert/5a40b085a1d117f8395c3ff6beeed85b.png)
Collection接口
Collection是一个接口,只是规定了一些方法,即作为一个容器就应该具有这些功能。在Collection中并没有任何的可以存储数据的地方,因此只是作为一个规范存在。
添加 (add)|删除(remove) 记数 (size) 包含 (contains)清空 (clear)是否空(isEmpty)
List接口 继承 Collection接口
List作为有序的Collection。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。同样是一个接口也只是提供了一些抽象方法,做出相应的规范
可以存放很多数据,List中的数据有序可以重复获取 get
查询对象出现的索引 indexOf(Object)
Set接口 继承 Collection接口
List作为Collection的子接口,也只是提供了一些抽象方法,做出相应的规范。
可以存放很多数据,但是set中的数据无序不能重复;如果前后数据重复,只存放一个。
Container
集合表示一组对象,称为其元素;
集合(容器)与数组的区别:
**共同点:**储存多个数据
不同点:
数组:1、引用数据类型,是对象数据;
2、定长,长度一旦确定不可改变;
3、只能储存多种类型相同的数据;
4、有序的,有索引;
集合:1、引用数据类型,是对象数据;
2、集合长度可跟随数据的增删进行改动;
3、可以可以任意引用数据类型的数据,存储的多个数据类型可以不同;
4、有些集合有序,有些集合无序,有些可以根据索引进行操作,有些不可以;
代码块:
public class Class001_Container{
public static void main(String[] args) {
//1)创建集合类型的实例
ArrayList list = new ArrayList();
//2)调用方法实现操作
list.add("张三");
System.out.println(list.size());
list.add(123);
System.out.println(list.size());
list.add(false);
System.out.println(list.size());
list.add('a');
System.out.println(list.size());
list.add(null);
System.out.println(list.size());
System.out.println(list);
}
}
自定义容器类型:
public class Class002_MyContainer {
public static void main(String[] args) {
//1.创建集合对象
MyContainer my = new MyContainer();
//2.调用方法操作集合
my.add("aaa");
System.out.println(my.size());
my.add("bbb");
System.out.println(my.size());
my.add("ccc");
System.out.println(my.size());
System.out.println(my.get(0));
System.out.println(my.get(1));
System.out.println(my.get(2));
}
}
//自定义容器类型
class MyContainer{
private String[] elementData; //记录集合存储的数据
private int size; //记录集合中已存储数据个数
public MyContainer() {
}
//添加数据
public void add(String value) {
//1)是否是第一次添加
if(elementData==null || size==0){
//是,创建数组长度为1
elementData = new String[1];
//数据放入数组
elementData[0] = value;
//长度+1
size++;
return;
}
//不是,创建新数组长度为原数组长度+1
//备份原数组地址
String[] temp = elementData;
elementData = new String[size+1];
//原数组数据拷贝到新数组
for(int i=0;i<size;i++){
elementData[i] = temp[i];
}
//新数据放在新数组最后
elementData[size++] = value;
//长度+1
}
//返回集合中数据个数
public int size(){
return this.size;
}
/**
* 根据索引获取数据
* @param index 索引
* @return index索引位置的数据
*/
public String get(int index) {
if(index<0 || index>=size){
throw new IndexOutOfBoundsException(index+"索引越界啦!!!");
}
return elementData[index];
}
//根据索引进行修改数据
//根据索引进行删除数据
}
Collection接口:
概念:
集合层次结构的根接口,规定了一些容器方法,不能存储数据,作为规范存在。
List子接口下的实现类有序可重复,set子接口下的类无序不可重。
遍历方式:
1、foreach
2、迭代器iterator
常用方法:
方法 | 返回值 | 作用 |
add(E e) | boolean | 向集合中添加一个元素,如果集合对象被添加操作改变了,则返回 true。E 是元素的数据类型 |
addAll(Collection c) | boolean | 向集合中添加一个元素,如果集合对象被添加操作改变了,则返回 true。E 是元素的数据类型 |
clear() | void | 清除集合中的所有元素,将集合长度变为 0。 |
contains(Object o) | boolean | 判断集合中是否存在指定元素 |
containsAll(Collection c) | boolean | 判断集合中是否包含集合 c 中的所有元素 |
6. isEmpty() | boolean | 判断集合是否为空 |
iterator() | Iterator | 返回一个 Iterator 对象,用于遍历集合中的元素 |
remove(Object o) | boolean | 从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,该方法只删除第一个符合条件的元素,该方法将返回 true。 |
removeAll(Collection c) | boolean | 从集合中删除所有在集合 c 中出现的元素(相当于把调用该方法的集合减去集合 c)。如果该操作改变了调用该方法的集合,则该方法返回 true。 |
retainAll(Collection c) | boolean | 从集合中删除集合 c 里不包含的元素(相当于把调用该方法的集合变成该集合和集合 c 的交集),如果该操作改变了调用该方法的集合,则该方法返回 true。 |
size() | int | 返回集合中元素的个数 |
toArray() | Object[] | 把集合转换为一个数组,所有的集合元素变成对应的数组元素。 |
ArrayList类
什么是ArrayList
ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。
优缺点
优点:允许对集合中的元素进行快速的随机访问。
缺点:向 ArrayList 中插入与删除元素的速度相对较慢。
三种构造器
1.ArrayList()
构造一个初始容量为十的空列表。
2.ArrayList(Collection<? extends E> c)
构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
3.ArrayList(int initialCapacity)
构造具有指定初始容量的空列表。
常用方法
方法 | 返回值 | 作用 |
get(int index) | E | 获取此集合中指定索引位置的元素,E 为集合中元素的数据类型 |
index(Object o) | int | 返回此集合中第一次出现指定元素的索引,如果此集合不包含该元素,则返回 -1 |
lastIndexOf(Object o) | int | 返回此集合中最后一次出现指定元素的索引,如果此集合不包含该元素,则返回 -1 |
set(int index, Eelement) | E | 将此集合中指定索引位置的元素修改为 element 参数指定的对象。此方法返回此集合中指定索引位置的原元素 |
subList(int fromlndex, int tolndex) | List | 返回一个新的集合,新集合中包含 fromlndex 和 tolndex 索引之间的所有元素。包含hanfromlndex 处的元素,不包含 tolndex 索引处的元素 |
代码
调用 Product 类的构造函数实例化三个对象,并将 Product 对象保存至 ArrayList 集合中。最后遍历该集合,输出商品信息。
subList() 方法的具体用法
LinkedList类
什么是LinkedList
LinkedList 类采用链表结构保存对象
优缺点
优点:便于向集合中插入或者删除元素。
缺点:检索集合中特定索引位置的元素的速度相对较慢
除了继承Collection/List接口的方法
方法 | 返回值 | 作用 |
addFirst(E e) | void | 将指定元素添加到此集合的开头 |
addLast(E e) | void | 将指定元素添加到此集合的末尾 |
getFirst() | E | 返回此集合的第一个元素 |
getLast() | E | 返回此集合的最后一个元素 |
removeFirst() | E | 删除此集合中的第一个元素 |
removeLast() | E | 删除此集合中的最后一个元素 |
代码
//仓库管理系统中要记录入库的商品名称,并且需要输出第一个录入的商品名称和最后—个商品名称。下面使用 LinkedList 集合来完成这些功能;
public class Test {
public static void main(String[] args) {
LinkedList<String> products = new LinkedList<String>(); // 创建集合对象
String p1 = new String("六角螺母");
String p2 = new String("10A 电缆线");
String p3 = new String("5M 卷尺");
String p4 = new String("4CM 原木方板");
products.add(p1); // 将 p1 对象添加到 LinkedList 集合中
products.add(p2); // 将 p2 对象添加到 LinkedList 集合中
products.add(p3); // 将 p3 对象添加到 LinkedList 集合中
products.add(p4); // 将 p4 对象添加到 LinkedList 集合中
String p5 = new String("标准文件夹小柜");
products.addLast(p5); // 向集合的末尾添加p5对象
System.out.print("*************** 商品信息 ***************");
System.out.println("\n目前商品有:");
for (int i = 0; i < products.size(); i++) {
System.out.print(products.get(i) + "\t");
}
System.out.println("\n第一个商品的名称为:" + products.getFirst());
System.out.println("最后一个商品的名称为:" + products.getLast());
products.removeLast(); // 删除最后一个元素
System.out.println("删除最后的元素,目前商品有:");
for (int i = 0; i < products.size(); i++) {
System.out.print(products.get(i) + "\t");
}
}
}
与ArrayList 类的区别
ArrayList 与 LinkedList 都是 List 接口的实现类,因此都实现了 List 的所有未实现的方法,只是实现的方式有所不同。
ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。
对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
集合
数组特点 | 存储多个相同数据类型 | 拥有索引,查询效率高 | 数组长度不变 |
集合特点 | 存储任意引用数据类型的数据 | 根据需求修改容量,增删数据 |
Collection
**List :**有序,可重复
ArrayList
有序,可重复
底层结构 : 数组
特点 : 查询效率高,增删效率低(涉及到了创建新数组,拷贝数据)
应用场景 : 适合应用在想要根据索引进行操作,有序可重复前提下,大量做查询,少量做增删
新增方法 :一些要操作索引的方法
遍历方式 :1) for 2) foreach 3)iterator
LinkedList
LinkedList : 并允许所有元素(包括null )。
有序,可重复
底层结构 : 双向链表
特点 : 增删效率高,查询效率低
应用场景 : 适合应用在大量做增删,少量做查询的情况
新增方法 : 新增了一些操作链表头尾的方法
遍历方式 : 1) for 2) foreach 3)iterator 4)listInterator
请注意,此实现不同步,线程不安全。
Vector (了解)
Set : 无序,不可重复,唯一的,去重的
HashSet
TreeSet
Map(映射的集合)
HashMap
TreeMap
Hashtable
Properties
Collections : 操作集合工具类
泛型
特点:
泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。泛型本质上是提供类型的“类型参数”,也就是参数化类型
jdk1.5新特性
参数化类型 : 把数据类型作为参数传递
使用方式 : <数据类型>
优点
增强程序的稳定性与可读性
严谨,健壮,避免类型转换异常的出现
便于后期维护
注意
定义类型的时候定义了泛型的使用,在使用类型的时候,才可以通过泛型传递数据类型
发生在编译期间,运行期间泛型的使用没有意义-->泛型擦除
泛型传递的类型必须为引用数据类型
手写linkedlist
/*
通过单向链表实现LinkedList类型 : 了解
toString : 返回对象的字符串表现形式
*/
public class Class003_MyLinked {
public static void main(String[] args) {
//空链表
MyLinkedList list = new MyLinkedList();
System.out.println(list.size());
//list.add("aa");
System.out.println(list.size());
//list.add("bb");
System.out.println(list.size());
//list.add("cc");
System.out.println(list.size())
//System.out.println(list.get(0));
//System.out.println(list.get(1));
//System.out.println(list.get(2));
//System.out.println(list.get(3));
System.out.println(list);
}
}
//自定义容器类型
class MyLinkedList{
private Node head; //记录链表的首节点
private int size; //记录集合中数据的个数
public MyLinkedList() {
}
/**
* 添加数据
* @param value 要添加的数据
*/
public void add(Object value) {
//创建新节点,存储要添加的数据
Node newNode = new Node(value,null);
//是否存在链表头
if(head==null && size==0){
//不存在,当前新节点作为链表头节点
head = newNode;
//长度+1
size++;
return;
}
//存在链表头节点,遍历,找到原链表的最后一个节点
Node temp = head; //临时变量,局部变量,作用就是用来遍历链表,指向最新的节点,直到最后一个节点结束(next属性值为null)
while(temp.getNext()!=null){
temp = temp.getNext();
}
//新节点挂上去
temp.setNext(newNode);
//长度+1
size++;
}
/**
* 获取集合中数据的个数
* @return 数据个数
*/
public int size(){
return this.size;
}
/**
* 根据索引获取数据
* @param index 索引
* @return 指定索引位置的数据
*/
public Object get(int index) {
if(index<0 || index>=size){
throw new IndexOutOfBoundsException(index+"越界了。。。");
}
//遍历
Node temp = head;
for(int i = 0;i<=size-1;i++){
if(i==index){ //当前temp所指向的节点的索引就是你要的索引
return temp.getData(); //返回当前节点存储的数据
}
temp = temp.getNext();
}
return null;
}
@Override
public String toString() {
if(head==null && size==0){
return "[]";
}
StringBuilder sb = new StringBuilder("[");
Node temp = head;
while(true){
sb.append(temp.getData());
sb.append(", ");
if(temp.getNext()==null){
break;
}
temp = temp.getNext(); //temp指向下一个节点
}
sb.delete(sb.length()-2,sb.length()); //结束索引位置一般不包含
sb.append("]");
return sb.toString();
}
}
//节点
class Node{
private Object data;
private Node next;
public Node() {
}
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}