10、容器Collection
容器
Collection接口
有两个子接口:list set
继承iterable
为什么要有集合呢?
数组存储数组的时候,长度一旦确定就无法改变,当我们存储未知长度的数据时,数组就满足不了我们的需求了。
集合是一个庞大的体系。
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
数据结构:在内存中开辟一个连续的内存空间。是一个线性表。线性表的存储结构:分为顺序存储结构和链式存储结构两种
该类也是实现了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
该类实现了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是如何保证元素的唯一性的呢?
答:是通过元素的两个方法,hashCode和equals方法来完成
如果元素的HashCode值相同,才会判断equals是否为true
如果元素的hashCode值不同,不会调用equals方法
LinkedHashSet
是HashSet的一个子类,在HashSet的基础上添加了链表的数据结构,使得set集合变成了有顺序,指的是添加顺序,并不是有下标,而那下标进行操作。
优点:比HashSet查找,添加,删除的效率低一点。因为数据结构更复杂了。还添加了顺序。
缺点:
TreeSet
采用二叉树(红黑树)的存储结构
优点:有序(排序后的升序)查询速度比List快
(按照内容查询)
缺点:查询速度没有HashSet快
注意:二叉树是一个升序的存储结构,所以要有比较的规则。
两种方式
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());
}
泛型方法
泛型接口
实现该接口的类可以确定泛型的类型,也可以不确定,那么创建对象的时候就要确定泛型的类型。泛型类型确定了,其他的也会跟着变成该类型。
//泛型接口
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 |

浙公网安备 33010602011771号