10、容器Collection

容器

Collection接口

有两个子接口:list set

继承iterable

为什么要有集合呢?

​ 数组存储数组的时候,长度一旦确定就无法改变,当我们存储未知长度的数据时,数组就满足不了我们的需求了。

集合是一个庞大的体系。

Conllection

Collection接口中的方法

collection是一个无序不唯一的集合。

集合作为容器应该具有的功能(增,删,改,查),
不一定全有。
集合的基本操作:增加,删除,判断,取出

序号 方法名 作用
1 add(Object obj) 添加,存储的是对象的引用
2 size() 容器中元素的实际个数
3 remove(Object obj)clear()removeAll(Collection c)retainAll(Collection c) 删除
4 contains(Object obj)isEmpty() 判断元素
5 iterator() 遍历元素

List接口

继承collection接口,是一种 有序 且 不唯一 的集合。

实现类:

ArrayList

数据结构:在内存中开辟一个连续的内存空间。是一个线性表。线性表的存储结构:分为顺序存储结构和链式存储结构两种

该类也是ArrayList实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%插入删除效率低

默认长度10, 之后的长度 10->16->25->38->58->88->...

低层是数组,可以说是一个动态的数组,查找元素效率高,添加,删除,插入效率低。

get(int index)//根据下标得到一个元素
add(index,e)//将e插入到index位置
set(index,e)//将e替换index位置中的元素
LinkedList

数据结构:不连续的内存空间的双向链表,一个数据分三个存储位置,(first,e,last)first指的是上一个储存的引用,e是元素,last是下个元素的引用,如果是最后一个,则为null

LinkedList

该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。例如:

Listlist=Collections.synchronizedList(newLinkedList(...));

LinkedList 查找效率低。

效率:增删快,查找慢

addFirst(e)//添加到第一个元素
add(e)//添加到最后一个元素
addLast(e);//添加到最后一个元素
getFirst();//得到第一个元素
getLast();//得到最后一个元素
offer(e);//将指定元素添加到末尾
offerFirst(e)//将指定元素添加到首
peek();//获取第一个元素,但不移除
peekFirst();获取第一个元素,如果此列表为空,返回null
poll();//获取并移除第一个元素
pollFirst()获取并移除第一个元素,如果此列表为空,返回null
pop();从堆栈中弹出第一个元素
push(e);将元素推进第一个元素
remove();移除表头元素
remove(index);移除指定位置的元素
remove(e);从此列表中移除首次出现的指定元素(如果存在)。
removeFirst();移除第一个元素并返回
set(index,e);将e替换index位置的元素
size();集合长度

set接口

在Collection的基础上添加了唯一性。但还是无序的,所以没有对下标进行操作。

接下来要学习三个set的实现类。

注意:

要想存储的数据是唯一的,必须要重写hashCode和equals方法。

set集合遍历有两种方式:

通过forEach:

for(String str : set){
str;//就是每一个元素
}

通过迭代器:

通过set调用iterator()得到Iterator接口对象,可以通过里面的三个方法来实现遍历
Iterator it = set.iterator();
while(it.hasNext()){//判断是否还有下一个元素
it.next();//得到一个元素,指向下一个位置
it.remove();//删除一个next得到的元素
}

HashSet

底层采用哈希表的结构进行存储。哈希表的储存结构:根据公式进行,比如:y=x%7;用余数分成7个区间,取的时候可以根据哈希码得到y,就可以得到存储在那个区间,这样就可以排除大量的数据,从而大大提高了根据内容查询效率。

优点:根据内容查询效率最高,添加,删除的效率最高。

缺点:无序。

数据结构:底层采用哈希表实现

HashSet

总结:
HashSet是如何保证元素的唯一性的呢?

答:是通过元素的两个方法,hashCode和equals方法来完成
如果元素的HashCode值相同,才会判断equals是否为true
如果元素的hashCode值不同,不会调用equals方法

LinkedHashSet

是HashSet的一个子类,在HashSet的基础上添加了链表的数据结构,使得set集合变成了有顺序,指的是添加顺序,并不是有下标,而那下标进行操作。

优点:比HashSet查找,添加,删除的效率低一点。因为数据结构更复杂了。还添加了顺序。

缺点:

LinkedHashSet

TreeSet

采用二叉树(红黑树)的存储结构
优点:有序(排序后的升序)查询速度比List快
(按照内容查询)
缺点:查询速度没有HashSet快

tree

注意:二叉树是一个升序的存储结构,所以要有比较的规则。

​ 两种方式

1.内部实现Comparable接口,实现CompareTo();在里面写比较的规则。

package treeSet;
/**
* 内部比较器
* @author lgx
*
*/
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
@Override
public int compareTo(Person person) {
return this.hashCode() - person.hashCode();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}

2.外部类的比较规则,外部类实现Comparator接口。实现compare();

package treeSet;
/**
* 外部比较器
* @author lgx
*
*/
public class Dog {
private String name;
private int age;
public Dog() {
super();
// TODO Auto-generated constructor stub
}
public Dog(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog [name=" + name + ", age=" + age + "]";
}
}
//外部实现接口的类
package treeSet;
import java.util.Comparator;
public class DogSort implements Comparator<Dog>{
@Override
public int compare(Dog o1, Dog o2) {
return o1.getAge()-o2.getAge();
}
}

测试的方法

package treeSet;
import java.util.TreeSet;
/**
* 使用TreeSet 的时候,必须有比较的规则,否则会出错
* 有内部比较器和外不比较器
* @author lgx
*
*/
public class Test {
public static void main(String[] args) {
// //外部比较器
// TreeSet<Dog> set = new TreeSet<Dog>(new DogSort());
// set.add(new Dog("旺财",1));
// set.add(new Dog("大黄",2));
// System.out.println(set);
//内部比较器
TreeSet<Person> set = new TreeSet<Person>();
set.add(new Person("bb",13));
set.add(new Person("aas",14));
set.add(new Person("cc",13));
set.add(new Person("cc",15));
System.out.println(set);
}
}

Map接口

存储的方式是以键值对的方式存储的,键是唯一的,而值可以相同。一个键对应一个值,比如通过书名来得到书的全部信息。

什么map的数据结构跟什么set的数据结构跟相似,主要在存储的时候多存入一个值,而键是完全跟Set一样。

HashMap

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

优点:超级快速的查询速度,如果有人问你什么数据结构可以达到O(1)的时间复杂度,没错是HashMap

​ 动态的可变长存储数据(和数组相比较而言)

缺点:需要额外计算一次hash值

​ 如果处理不当会占用额外的空间

LinkedHashMap

是HashMap的一个子类,在他之前添加了存储顺序

TreeMap

是一个二叉树的存储结构

Iterator接口

是一个Iterable接口中一个iterator()方法的一个返回值类型。

Iterator是一个迭代器,里面有三个方法:hasNext()判断是否还有下一个元素。next()返回当前的元素,指向下个元素。remove();移除返回的元素

所有的集合类均未提供相应的遍历方法,而是把遍历交给迭代器完成。迭代器为集合而生,专门实现集合遍历。

Collections类

是一个工具类,提供了很多静态的 方法,是为了方法操作Collection接口的实现类。

主要的方法:

​ addAll(Collection con,T....elements); 将所有指定元素添加到指定 collection 中。

​ binarySearch();二分查找

​ sort();对集合进行排序,要定义排序的规则

​ reverse();倒序排序

​ fill();替换

copy(...);将所有元素从一个列表复制到另一个列表。执行此操作后,目标列表中每个已复制元素的索引将等同于源列表中该元素的索引。目标列表的长度至少必须等于源列表。如果目标列表更长一些,也不会影响目标列表中的其余元素。

forEach

增强for循环,根据集合引用遍历,可以直接得到对象,一般用做遍历。

For-each循环

增强的for循环,遍历array或Collection的时候相当简便
无需获得集合和数组的长度,无需使用索引访问元素,无需循环条件
遍历集合时底层调用Iterator完成操作

For-each缺陷

数组:
不能方便的访问下标值
不要在for-each中尝试对变量赋值,只是一个临时变量
集合:
与使用Iterator相比,不能方便 的删除集合中的内容

泛型

泛型是程序设计语言的一种特性。在编写代码的时候,不确定该使用什么类型的时候,我们可以先定义一个泛型,那些部分在使用前必须要作出声明。这样可以达到代码的复用提高软件的开发效率。泛型是一个引用类型,是堆对象,主要引用了类型参数的概念。

有泛型类,泛型方法,泛型接口。

泛型类

public class A<T> { // 泛型类:定义类的时候指定类型形参T,在类里面T就可以当成类型使用
private T a;
public T getA() {
return a;
}
public void setA(T a) {
this.a = a;
}
}

继承泛型类的几种方式

class B1 extends A<String> {}
class B2<E> extends A<String> {}
class B3<E> extends A<E> {}
class B4<E1, E2> extends A<E1> {}

使用泛型类

public static void main(String[] args) {
B1 b1 = new B1();
b1.setA("b1");
System.out.println(b1.getA());
A<String> a1 = new B1();
a1.setA("a1");
System.out.println(a1.getA());
//B2<?> b2 = new B2<String>();
//B2<String> b2:声明变量时已经指定了B2的类型形参E为String,
//new B2<>():创建对象时可以使用菱形语法(泛型推断)
B2<String> b2 = new B2<>();//菱形语法
b2.setA("b2");
System.out.println(b2.getA());
// 无法通过A<String>推断出B2的类型形参E的类型,不可以使用菱形语法
A<String> a2 = new B2<Object>();
a2.setA("a2");
System.out.println(a2.getA());
B3<String> b3 = new B3<>();//菱形语法
b3.setA("b3");
System.out.println(b3.getA());
A<String> a3 = new B3<>();//菱形语法
a3.setA("a3");
System.out.println(a3.getA());
}

泛型方法

泛型方法

方法f

泛型接口

实现该接口的类可以确定泛型的类型,也可以不确定,那么创建对象的时候就要确定泛型的类型。泛型类型确定了,其他的也会跟着变成该类型。

//泛型接口
package test;
public interface DogInterface<E> {
E test();
void test02(E e);
}
//接口实现类
package test;
public class DogImpl<E> implements DogInterface<E>{//可以不确定,也可以确定
@Override
public E test() {
// TODO Auto-generated method stub
return null;
}
@Override
public void test02(E e) {
// TODO Auto-generated method stub
}
}
//测试
package test;
public class Test {
public static void main(String[] args) {
DogImpl dog = new DogImpl<String>();//创建对象的时候就要确定类型
}
}

Comparable接口

问题:上面的算法根据什么确定集合中对象的“大小”顺序?
所有可以“排序”的类都实现了java.lang.Comparable 接口,Comparable接口中只有一个方法
public int compareTo(Object obj);
该方法:
返回 0 表示 this == obj
返回正数 表示 this > obj
返回负数 表示 this < obj

实现了Comparable 接口的类通过实现 comparaTo 方法从而确定该类对象的排序方式。

总结

名称 存储结构 顺序 唯一性 查询效率 添加/删除效率
ArrayList 顺序表 有序(添加) 不唯一 索引查询效率高
LinkedList 链表 有序(添加) 不唯一 下标查低 最高
HashSet 哈希表 无序 唯一 最高 最高
HashMap 哈希表 Key无序 Key唯一 最高 最高
LinkedHashSet 哈+链 有序(添加) 唯一 最高 最高
LinkedHashMap 哈+链 Key有序( 添加) Key唯一 最高 最高
TreeSet 二叉树 有序(升序) 唯一 中等 中等
TreeMap 二叉树 有序(升序) Key唯一 中等 中等
特性 Collection Map
无序不唯一 Collection Map.values()
有序不唯一 ArrayList LinkedList
无序唯一 HashSet HashMapkeySet
有序唯一 LinkedHashSet TreeSet LinkedHashMapkeySet TreeMapkeySet
posted @   站着说话不腰疼  阅读(57)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示