前言
Collection 接口的层次结构图:
一、集合概述
所有的集合类和集合接口都在 java.util 包下。
集合实际上就是一个容器。可以来容纳其它类型的数据,可以一次容纳多个对象。(数组其实就是一个集合。)
集合不能直接存储基本数据类型,也不能直接存储 java 对象,集合当中存储的都是 java 对象的内存地址。(或者说集合中存储的是引用。)
在 java 中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。(例如:数组、二叉树、链表、哈希表...以上这些都是常见的数据结构。)
在 java 中集合分为两大类:
一类是单个方式存储元素:单个方式存储元素,这一类集合中超级父接口:java.util.Collection;一类是以键值对儿的方式存储元素以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;
二、Collection 详述
Collection 是 List 和 Set 的父接口
Collection 接口中的常用方法:
boolean add(E e) 向集合中添加元素
int size();获取集合中的元素个数
void clear();清空集合
boolean contains(Object o) 判断当前集合是否含有o元素
boolean remove(Object o) 删除集合中的元素
boolean isEmpty() 判断集合是否为空
Object[] toArray() 把集合转换为数组
示例代码(1):
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionText {
public static void main(String[] args) {
Collection collection = new ArrayList();
//向集合中添加元素
boolean b1 = collection.add(100);
boolean b2 = collection.add("你好,java");
boolean b3 = collection.add("中国");
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);
//获取集合中的元素个数
int i1 = collection.size();
System.out.println(i1);
//collection.clear();
//System.out.println(collection.size());
boolean b = collection.contains(100);
System.out.println(b);
//删除集合中的元素
collection.remove("中国");
System.out.println(collection.size());
//判断集合是否为空
System.out.println(collection.isEmpty());
collection.clear();
System.out.println(collection.isEmpty());
collection.add("我");
collection.add("是");
collection.add("中");
collection.add("国");
collection.add("人");
//转换为数组
Object[] objects=collection.toArray();
for(int i=0;i<objects.length;i++){
Object o=objects[i];
System.out.println(o);
}
System.out.println(Arrays.toString(objects));
}
}
复制代码
运行结果:
true
true
true
3
true
2
false
true
我
是
中
国
人
[我, 是, 中, 国, 人]
Process finished with exit code 0
复制代码
注:Collection 在使用泛型之前,可存放 Object 的所有子类,在使用泛型之后,只能存某个具体的类型。
集合的迭代(遍历)
迭代器是一个对象
第一步:获取集合对象的迭代器对象第二部:通过获取的迭代器对象进行迭代(遍历)
boolean hasNext()
; true:有元素迭代 false:无元素迭代E next() ;
返回迭代的下一个元素
示例代码(2):
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionText01 {
public static void main(String[] args) {
//创建集合
Collection c1=new ArrayList();
c1.add(100);
c1.add("abc");
c1.add("def");
c1.add(new Object());
//第一步:获取集合对象的迭代器对象iterator
Iterator it=c1.iterator();
//第二步通过获取的迭代器对象进行集合遍历/迭代。
while (it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}
}
}
复制代码
运行结果:
100
abc
def
java.lang.Object@723279cf
复制代码
1.集合结果发生改变,迭代器需要重新获取 2.迭代过程中不能调用 remove()方法删除元素,会出现异常(ConcurrentModificationException)3.迭代元素的过程中使用迭代器的 remove()方法删除元素
示例代码(3):
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionText05 {
public static void main(String[] args) {
Collection c=new ArrayList();
//ConcurrentModificationException异常!!!
//Iterator it=c.iterator();
c.add(1);
c.add(2);
c.add(3);
Iterator it=c.iterator();
while (it.hasNext()){
Object o=it.next();
// 没有更新迭代器
//c.remove();直接通过集合,没有通知迭代器(导致迭代器快照与原集合状态不同)
//更新迭代器 。
it.remove(); //删除的是迭代器指向的当前对象。
System.out.println(o);
}
System.out.println(c.size());
}
}
复制代码
运行结果:
三、List 详述
他们都是有顺序的,也就是放进去是什么顺序,取出来还是什么顺序,也就是基于线性存储,可以看作是一个可变数组
List 接口的常用方法:
void add(int index, E element) 在列表中指定的位置上插入指定的元素
E get(int index) 根据下标获取元素
int indexOf(Object o) 返回此列表中指定元素的第一个出现的索引
int lastIndexOf(Object o) 返回此列表中指定元素的最后一个发生的索引
E remove(int index) 移除此列表中指定下标的元素
E set(int index, E element) 用指定元素替换此列表中指定位置的元素
示例代码(4):
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListText01 {
public static void main(String[] args) {
//创建list类型集合
List list=new ArrayList();
//添加元素,将指定的元素到这个列表的末尾
list.add("a");
list.add("b");
list.add("c");
//在列表中指定的位置上插入指定的元素(效率低!!!)
list.add(1,"ooo");
//迭代器
Iterator it=list.iterator();
while(it.hasNext()){
Object o=it.next();
System.out.println(o);
}
System.out.println("=================================");
//根据下标获取元素
Object o=list.get(0);
System.out.println(o);
//list集合特有的遍历方法!!
for(int i=0;i<list.size();i++){
System.out.println( list.get(i));
}
// 返回此列表中指定元素的第一个出现的索引
System.out.println(list.indexOf("b"));
//返回此列表中指定元素的最后一个发生的索引
System.out.println(list.lastIndexOf("c"));
System.out.println("=============================");
//移除此列表中指定位置的元素
list.remove(1);
System.out.println(list.size());//3
//用指定元素替换此列表中指定位置的元素
list.set(0,"n");
System.out.println(list.get(0));
}
}
复制代码
运行结果:
a
ooo
b
c
==============================
a
a
ooo
b
c
2
3
=============================
3
n
复制代码
ArrayList 类
ArrayList 集合优化:尽可能少的扩容,数组扩容效率低。
优点:查询数据比较快,检索效率高缺点:添加和删除数据比较慢,无法存大数据量(向数组末尾添加元素效率高)
示例代码(5):
import java.util.*;
public class ArrayListText01 {
public static void main(String[] args) {
//初始化容量为10
List list1=new ArrayList();
//初始化容量为20
List list2=new ArrayList(20);
Collection c=new ArrayList();
c.add("cccc");
c.add("bvncm");
c.add("mnkj");
Iterator it=c.iterator();
while (it.hasNext()){
Object o= it.next();
System.out.println(o);
}
}
}
复制代码
运行结果:
LinkedList 类
LinkedList 底层是基于链表数据结构。
LinkedList 集合优缺点:
优点:添加和删除数据比较快,随机增删效率高缺点:查询数据比较慢,检索效率低。
Vector 类
Vector 初始化容量是 10.
扩容为原容量的 2 倍。
Vector 底层是数组。
Vector 底层是线程安全的,但是效率低
泛型机制(jdk1.5 之后新特性)
优点:1.集合中存储元素类型统一 2.集合中去除掉元素是泛型指定的类型,无需进行向下转型。缺点:导致集合中元素缺乏多样性
List<String> list = new ArrayList<>();
类型自动推断!
复制代码
示例代码(6):
使用泛型之前
自定义类:
class Animal{
public void run(){
System.out.println("动物在移动!!");
}
}
class Cat extends Animal{
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
class Dog extends Animal{
public void fly(){
System.out.println("狗在飞");
}
}
复制代码
测试类:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* jdk5.0之后的特性:泛型
*/
public class GenericText {
//使用泛型之前
public static void main(String[] args) {
List list = new ArrayList();
Cat cat = new Cat();
Dog dog = new Dog();
list.add(cat);
list.add(dog);
Iterator it = list.iterator();
while (it.hasNext()) {
Object o = it.next();
if (o instanceof Animal) {
((Animal) o).run();
}
}
}
}
复制代码
运行结果:
使用泛型之后
测试类:
public class GenericText {
public static void main(String[] args) {
//使用泛型List<Animal>后,表示List集合只能存储Animal类型的元素
//泛型指定集合中存储的数据类型
List<Animal> list = new ArrayList<Animal>();
Cat cat = new Cat();
Dog dog = new Dog();
list.add(cat);
list.add(dog);
//表示迭代器迭代Animal类型
Iterator<Animal> it = list.iterator();
while (it.hasNext()) {
//使用泛型后,迭代器每一次返回的数据是Animal类型
Animal a = it.next();
//无需强转,直接调用
a.run();
if (a instanceof Cat) {
((Cat) a).catchMouse();
} else if (a instanceof Dog) {
((Dog) a).fly();
}
}
}
}
复制代码
运行结果:
一起找不同吧!!!!!
四、Set 详述
无序表示存进去是这个顺序,取出来就不一定是这个顺序了,另外 Set 集合中元素没有下标。Set 集合中的元素还不能重复。
HashSet 类
HashSet 中的数据是无序的不可重复的。HashSet 按照哈希算法存取数据的,具有非常好性能,它的工作原理是这样的,当向 HashSet 中插入数据的时候,他会调用对象的 hashCode 得到该对象的哈希码,然后根据哈希码计算出该对象插入到集合中的位置。
HashSet 不是同步的;
集合元素值可以是 null;
如果 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals 方法比较 true,但它们的 hashCode 方法返回的值不相等,HashSet 将会把它们存储在不同位置,依然可以添加成功。HashSet 集合判断两个元素的标准是两个对象通过 equals 方法比较相等,并且两个对象的 hashCode 方法返回值也相等。
特别是向 HashSet 或 HashMap 中加入数据时必须同时覆盖 equals 和 hashCode 方法,应该养成一种习惯覆盖 equals 的同时最好同时覆盖 hashCode
两个对象 equals 相等,那么它的 hashcode 相等两个对象 equals 不相等,那么它的 hashcode 并不要求它不相等,但一般建议不相等 hashcode 相等不代表两个对象相等(采用 equals 比较)
哈希表
示例代码(7):
==没有重写 hashCode 和 equals 之前==
自定义类:
public class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
复制代码
测试类:
import java.util.HashSet;
import java.util.Set;
public class HashMapText02 {
public static void main(String[] args) {
Student s1 = new Student("zhangsan");
Student s2 = new Student("zhangsan");
System.out.println(s1.equals(s2));
//重写hashCode之前
System.out.println("s1hashCode值:" + s1.hashCode());
System.out.println("s2hashCode值:" + s2.hashCode());
Set<Student> set=new HashSet<>();
set.add(s1);
set.add(s2);
System.out.println(set.size());//2
}
}
复制代码
运行结果:
false
s1hashCode值:284720968
s2hashCode值:122883338
2
复制代码
重写 hashCode 和 equals 之后
自定义类:
public class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//equals方法
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
//hashCode方法
public int hashCode() {
return Objects.hash(name);
}
}
复制代码
测试类:
import java.util.HashSet;
import java.util.Set;
public class HashMapText02 {
public static void main(String[] args) {
Student s1 = new Student("zhangsan");
Student s2 = new Student("zhangsan");
System.out.println(s1.equals(s2));
//重写hashCode之后
System.out.println("s1hashCode值:" + s1.hashCode());
System.out.println("s2hashCode值:" + s2.hashCode());
Set<Student> set=new HashSet<>();
set.add(s1);
set.add(s2);
System.out.println(set.size());//1
}
}
复制代码
运行结果:
true
s1hashCode值:-1432604525
s2hashCode值:-1432604525
1
复制代码
找呀找呀,找不同。
TreeSet 类
第一种:Java 提供了一个 Comparable 接口,该接口里定义了一个 compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类必须实现该方法,实现接口的类就可以比较大小了。当调用一个一个对象调用该方法与另一个对象进行比较 obj1.compareTo(obj2)如果返回 0 表示两个对象相等;如果返回正整数则表明 obj1 大于 obj2,如果是负整数则相反。第二种:在构造 TreeSet 或 TreeMap 集合时给他一个比较器对象。比较规则自己写!!!
当比较规则不会发生改变时或比较规则只用一个时建议实现 Comparable 接口当比较规则有多个,并且需要多个比较规则之间进行切换,建议使用与 Comparator 编写比较器可以改变规则!!!
Comparator 区别符合 OCT 原则
红黑树(自平衡二叉树)
示例代码(8):
import java.util.TreeSet;
public class TreeMapText01 {
public static void main(String[] args) {
//
TreeSet<String> ts=new TreeSet();
ts.add("zhangsan");
ts.add("wangwu");
ts.add("make");
ts.add("langlang");
for (String s: ts){
System.out.println(s);
}
System.out.println("=============================");
TreeSet<Integer> ts2=new TreeSet();
ts2.add(200);
ts2.add(300);
ts2.add(600);
ts2.add(14);
for (Integer i:ts2){
System.out.println(i);
}
}
}
复制代码
运行结果:
langlang
make
wangwu
zhangsan
=============================
14
200
300
600
Process finished with exit code 0
复制代码
从以上结果可以看出,String 类和 Integer 类都实现了这个接口。示例代码(9):
实现 Comparable 接口
Customer 类:
class Customer implements Comparable<Customer>{
int age;
public Customer(int age) {
this.age = age;
}
//需要在此方法写比较的逻辑,或者说出比较规则,按照什么进行比较!!
//比较规则自己定
public int compareTo(Customer o) {//c1.comperTo(c2);
//this是c1,o是c2
//c1与c2比较,就是this与c比较
// return this.age-o.age;
return o.age-this.age;
}
@Override
public String toString() {
return "Customer{" + "age=" + age + '}';
}
}
复制代码
测试类:
import java.util.TreeSet;
public class TreeSetText03 {
public static void main(String[] args) {
Customer p = new Customer(23);
Customer p2 = new Customer(100);
Customer p3 = new Customer(30);
Customer p4 = new Customer(65);
Customer p5 = new Customer(46);
TreeSet<Customer> treeSet = new TreeSet<>();
treeSet.add(p);
treeSet.add(p2);
treeSet.add(p3);
treeSet.add(p4);
treeSet.add(p5);
for (Customer c : treeSet) {
System.out.println(c);
}
}
}
复制代码
运行结果:
Customer{age=100}
Customer{age=65}
Customer{age=46}
Customer{age=30}
Customer{age=23}
复制代码
示例代码(10):
比较器进行排序
WuGui 类:
class WuGui {
int age;
public WuGui(int age) {
this.age = age;
}
@Override
public String toString() {
return "WuGui{" +
"age=" + age +
'}';
}
}
复制代码
比较器:
//单独在这里编写一个比较器
//比较实现java.util.Comparator接口。(Comparable是java.lang包下的,Comparator接口是java.util包下)
class WuguiComparator implements Comparator<WuGui> {
public int compare(WuGui o1, WuGui o2) {
return o1.age - o2.age;
}
}
复制代码
测试类:
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetText05 {
public static void main(String[] args) {
//创建TreeSet时,需要使用比较器
//TreeSet<WuGui> wuGuis=new TreeSet<>();这样不行,没有通过构造方法构造一个比较器进去
//给构造方法添加一个比较器
TreeSet<WuGui> wuGuis = new TreeSet<>(new WuguiComparator());
wuGuis.add(new WuGui(200));
wuGuis.add(new WuGui(30));
wuGuis.add(new WuGui(50));
wuGuis.add(new WuGui(10));
wuGuis.add(new WuGui(100));
for (WuGui w : wuGuis) {
System.out.println(w);
}
}
}
复制代码
运行结果:
WuGui{age=10}
WuGui{age=30}
WuGui{age=50}
WuGui{age=100}
WuGui{age=200}
复制代码
改进上面测试类(使用匿名内部类)代码如下:
public class TreeSetText05 {
public static void main(String[] args) {
//第三种!! //使用匿名内部类的方式 这个类没有名字,直接new接口!!
TreeSet<WuGui> wuGuis = new TreeSet<>(new Comparator<WuGui>() {
@Override
public int compare(WuGui o1, WuGui o2) {
return o1.age - o2.age;
}
});
wuGuis.add(new WuGui(200));
wuGuis.add(new WuGui(30));
wuGuis.add(new WuGui(50));
wuGuis.add(new WuGui(10));
wuGuis.add(new WuGui(100));
for (WuGui w : wuGuis) {
System.out.println(w);
}
}
}
复制代码
评论