一、集合介绍
1、集合的概念
集合有时也称为容器,它可以将系列元素组合成一个单元,用于存储、提取、管理数据。面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,存储对象,集合是存储对象最常用的一种方式。集合的出现就是为了持有对象。集合中可以存储任意类型的对象, 而且长度可变。在程序中有可能无法预先知道需要多少个对象, 那么用数组来装对象的话, 长度不好定义, 而集合解决了这样的问题。
2、集合和数组的区别
数组和集合类都是容器。
数组长度是固定的,集合长度是可变的。
数组中可以存储基本数据类型,集合只能存储对象。数组中存储数据类型是单一的,集合中可以存储任意类型的对象。
集合类的特点,用于存储对象,长度是可变的,可以存储不同类型的对象。
集合家族
二、 Collection接口
Collection中描述的是集合共有的功能。
学习Collection中的共性方法,多个容器在不断向上抽取就出现了该体系。发现Collection接口中具有所有容器都具备的共性方法。查阅API时,就可以直接看该接口中的方法。并创建其子类对象对集合进行基本应用。当要使用集合对象中特有的方法,在查看子类具体内容。
查看api 文档Collection在java.util 中(注意是大写Collection)
注意在现阶段遇到的 E、T 之类的类型,需要暂时理解为object。因为涉及到了泛型。
Collection接口所定义的方法:
|
测试:可以创建一个具体的实现类,例如:
Collection c = new ArrayList();
- 增加
示例代码
public static void main(String[] args) {
Collection list = new ArrayList();
// 增加:add() 将指定对象存储到容器中
list.add("计算机网络");
list.add("现代操作系统");
list.add("java编程思想");
System.out.println(list);
// [计算机网络, 现代操作系统, java编程思想]
// 增加2 将list容器元素添加到list2容器中
Collection list2 = new ArrayList();
list2.add("java核心技术");
list2.addAll(list);
list2.add("java语言程序设计");
System.out.println(list2);
// [java核心技术, 计算机网络, 现代操作系统, java编程思想, java语言程序设计]
}
|
- 删除:
示例代码
// 删除1 remove
boolean remove = list2.remove("java核心技术");
System.out.println(remove); // true
System.out.println(list2); //
//删除2 removeAll() 将list中的元素删除
boolean removeAll = list2.removeAll(list);
System.out.println(removeAll);//true
System.out.println(list2);//[java语言程序设计]
|
- 修改:
示例代码
public static void main(String[] args) {
Collection list = new ArrayList();
// 增加:add() 将指定对象存储到容器中
list.add("计算机网络");
list.add("现代操作系统");
list.add("java编程思想");
list.add("java核心技术");
list.add("java语言程序设计");
System.out.println(list);
// 修改 clear() 清空集合中的所有元素
list.clear();
System.out.println(list); //[]
}
|
- 判断:
示例代码
|
- 获取:
示例代码
public static void main(String[] args) {
Collection list = new ArrayList();
// 增加:add() 将指定对象存储到容器中
list.add("计算机网络");
list.add("现代操作系统");
list.add("java编程思想");
list.add("java核心技术");
list.add("java语言程序设计");
System.out.println(list);
// 获取 集合容器的大小
int size = list.size();
System.out.println(size);
}
|
6、自定义类的对象在集合中的操作
定义一个Person类,完成在Collection中的增删改查操作。
示例代码
class Person{
private int id;
private String name;
private int age;
public Person(){
}
public Person(int id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
}
public class TestCollection2 {
public static void main(String[] args) {
Collection c = new ArrayList();
Person p1 = new Person(1,"小毛",20);
Person p2 = new Person(2,"小邓",21);
Person p3 = new Person(3,"小江",22);
c.add(p1);
c.add(p2);
c.add(p3);
System.out.println(c);
//c.remove(p1);
//Person p4 = new Person(1,"小毛",20);
Person p4 = p1;
c.remove(p4);
System.out.println(c);
/*
System.out.println(c.size());
c.clear();
System.out.println(c.size());
*/
}
}
|
7、Collection接口的子接口
Collection接口有两个子接口:
List(链表|线性表)
Set(集合)
三、Set接口
Set:注重独一无二的性质,该体系集合可以知道某物是否已近存在于集合中,不会存储重复的元素。用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。
该集合中没有特有的方法,直接继承自Collection。
Set集合的特点:无序、数据不重复。
主要包含的实现类有:HashSet、TreeSet、LinkedHashSet ...
- HashSet
(1)HashSet结构和原理
用Hash技术实现的Set结构。
由于Set集合是不能存入重复元素的集合。那么HashSet也是具备这一特性的。
HashSet如何检查重复?HashSet会通过元素的hashcode()和equals方法进行判断元素是否重复。
当你试图把对象加入HashSet时,HashSet会使用对象的hashCode来判断对象加入的位置。同时也会与其它已经加入的对象的hashCode进行比较,如果没有相等的hashCode,HashSet就会假设对象没有重复出现。
简单一句话,如果对象的hashCode值是不同的,那么HashSet会认为对象是不可能相等的。
因此我们自定义类的时候需要重写hashCode,来确保对象具有相同的hashCode值。
如果元素(对象)的hashCode值相同,是不是就无法存入HashSet中了? 当然不是,会继续使用equals 进行比较.如果 equals为true,那么HashSet认为新加入的对象重复了,所以加入失败。如果equals 为false,那么HashSet 认为新加入的对象没有重复,新元素可以存入。
总结:
元素的哈希值是通过元素的hashcode方法来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法。如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。
哈希值相同,equals为false的元素是怎么存储呢?就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。
散列技术的原理:
把对象的主键直接用一个固定的公式计算,得出存储位置的方法。
优点是:可以快速命中搜索的目标。
|
(2)添加数据到HashSet
1 基本数据类型存入HashSet中
示例代码
HashSet set = new HashSet(); set.add(100); set.add(200); set.add(300); set.add(400); set.add(500); set.add(400); // 不会存入重复数据 |
2 字符串类型数据存入HashSet中
示例代码
HashSet set = new HashSet(); set.add("aaa"); set.add("bbb"); set.add("ccc"); set.add("ddd"); set.add("aaa");// 不会存入重复数据 |
3 自定义类型存入HashSet中
示例代码
class Student{ private String name; private int age;
public Student(){ }
public Student(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; } } public class TestHashSet {
public static void main(String[] args) {
HashSet hs = new HashSet();
Student s1 = new Student("张三",20); Student s2 = new Student("李四",21); Student s3 = new Student("王五",23); Student s4 = new Student("张三",20); Student s5 = s1;
hs.add(s1); hs.add(s2); hs.add(s3); hs.add(s4); hs.add(s5); //s5和s1 指向同一个对象,所以hashCode相同,不会被存入
System.out.println(hs.size()); System.out.println(hs); } } |
修改需求。只要Student的name和age值相同,就认为是重复数据,不能加入到HashSet中,如何处理? 重写Student类的hashCode和equals方法。
示例代码
class Student{ private String name; private int age; public Student(){
} public Student(String name, int age) { 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 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; Student other = (Student) 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 boolean equals(Object obj){
Student s = (Student)obj;
if(this.name.equals(s.getName()) && this.age==s.getAge()){
return true;
}else{
return false; }
}
public int hashCode(){
return this.name.hashCode()+age; } */ } public class TestHashSet {
public static void main(String[] args) {
HashSet hs = new HashSet();
Student s1 = new Student("张三",20); Student s2 = new Student("李四",21); Student s3 = new Student("王五",23); Student s4 = new Student("张三",20);
System.out.println(s1.hashCode()); System.out.println(s4.hashCode());
System.out.println(s1.equals(s4)); //Student s5 = s1;
hs.add(s1); hs.add(s2); hs.add(s3); hs.add(s4); //hs.add(s5); //s5和s1 指向同一个对象,所以hashCode相同,不会被存入
System.out.println(hs.size()); System.out.println(hs); } } |
2、泛型(入门)
JDK1.4之前类型不明确,装入集合的类型都被当作Object对待,从而失去自己的类型。从集合中取出时往往需要类型转换,效率低下,易出错。
解决办法: JDK1.5开始,增加了泛型的功能。
定义集合时,同时定义集合中元素的类型。
好处
增强程序的可读性和稳定性。
Set<String> set = new HashSet<String>();
这个集合中就只能添加String类型的数据。
泛型:可以简单理解为限制了集合中存入的数据的类型。
示例代码
public class Demo06_泛型入门 {
public static void main(String[] args) { /* Set s = new HashSet(); s.add("aaa"); s.add("bbb"); s.add(111); s.add(222); s.add(new Date()); s.add(new User(1,"aaa",20)); System.out.println(s.size());
for(Object obj:s){ System.out.println(obj); } */ Set<String> s = new HashSet<>(); s.add("aaa"); s.add("bbb"); s.add("ccc"); //s.add(111);
Set<Integer> s2 = new HashSet<>(); s2.add(1); s2.add(2); //s2.add("aaa"); } } |
3、迭代器和增强型for循环
(1)迭代器
为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素。例如删除和获取集合中的元素。该对象就叫做迭代器(Iterator)。
对 Collection 进行迭代的类,称其为迭代器。还是面向对象的思想,专业对象做专业的事情,迭代器就是专门取出集合元素的对象。但是该对象比较特殊,不能直接创建对象(通过new),该对象是以内部类的形式存在于每个集合类的内部。
如何获取迭代器?Collection接口中定义了获取集合类迭代器的方法(iterator()),所以所有的Collection体系集合都可以获取自身的迭代器。
正是由于每一个容器都有取出元素的功能。这些功能定义都一样,只不过实现的具体方式不同(因为每一个容器的数据结构不一样)。所以对共性的取出功能进行了抽取,从而出现了Iterator接口。而每一个容器都在其内部对该接口进行了内部类的实现,也就是将取出方式的细节进行封装。
Iterable接口:
Jdk1.5之后添加的新接口, Collection的父接口。实现了Iterable的类就是可迭代的。并且支持增强for循环。该接口只有一个方法,即获取迭代器的方法iterator(),可以获取每个容器自身的迭代器Iterator。(Collection)集合容器都需要获取迭代器(Iterator)。于是在5.0后又进行了抽取,将获取容器迭代器的iterator()方法放入到了Iterable接口中。Collection接口继承了Iterable,所以Collection体系都具备获取自身迭代器的方法。只不过每个子类集合都进行了重写(因为数据结构不同)。
Iterator接口:
Iterator iterator() 返回该集合的迭代器对象
该类主要用于遍历集合对象,该类描述了遍历集合的常见方法 1:java.lang. Itreable ---| Itreable 接口 实现该接口可以使用增强for循环 ---| Collection 描述所有集合共性的接口 ---| List接口 可以有重复元素的集合 ---| Set接口 不可以有重复元素的集合 |
public interface Iterable<T>
Itreable 该接口仅有一个方法,用于返回集合迭代器对象。
1: Iterator<T> iterator() 返回集合的迭代器对象
Iterator接口定义的方法
Itreator 该接口是集合的迭代器接口类,定义了常见的迭代方法 1:boolean hasNext() 判断集合中是否有元素,如果有元素可以迭代,就返回true。 2: E next() 返回迭代的下一个元素,注意: 如果没有下一个元素时,调用next元素会抛出NoSuchElementException 3: void remove() 从迭代器指向的集合中移除迭代器返回的最后一个元素(可选操作)。 |
思考:为什么next方法的返回类型是Object的呢? 为了可以接收任意类型的对象,那么返回的时候,不知道是什么类型的就定义为object。
使用Iterator遍历HashSet集合
示例代码
Set<String> set = new HashSet<String>(); set.add("aaa"); set.add("bbb"); set.add("ccc"); set.add("ddd");
Iterator<String> it = set.iterator(); /* it.hasNext(); // 判断是否有下一个元素 it.next(); // 取到元素 */ while(it.hasNext()){ String str = it.next(); System.out.println(str); } |
原理图:
|
(2)增强型for循环
只要实现了Iterable接口的集合,就可以使用增强型for循环进行遍历。
示例代码
Set<String> set = new HashSet<String>(); set.add("aaa"); set.add("bbb"); set.add("ccc"); set.add("ddd");
for(String str:set){ System.out.println(str); } |
4、HashSet案例
String[] names = {"A","B","C","D","E","F","G"};
String[] scores = {"A,数学,89", "A,语文,86","B,数学,88","B,英语,98","C,英语,67","G,数学,60"};
求names中,谁全部缺考?
提示:使用removeAll方法。
示例代码
String[] names = {"A","B","C","D","E","F","G"}; String[] scores = {"A,数学,89", "A,语文,86","B,数学,88","B,英语,98","C,英语,67","G,数学,60"};
Set<String> namesSet = new HashSet<>(); for(String name:names){ namesSet.add(name); }
Set<String> scoresSet = new HashSet<>();
for(String str:scores){ scoresSet.add(str.split(",")[0]); } namesSet.removeAll(scoresSet);
Iterator<String> it = namesSet.iterator(); while(it.hasNext()){ System.out.println(it.next()); } |
5、TreeSet类
TreeSet是一个可排序集合,基于红黑树(自平衡二叉树),默认按元素的自然顺序升序排列。
示例代码
public static void main(String[] args) { Set<Integer> set = new TreeSet<>(); //会对存入的数据自动排序(升序) set.add(2); set.add(5); set.add(3); set.add(1); set.add(4); set.add(5); System.out.println("集合中元素个数:"+set.size()); for(int x:set){ System.out.println(x); } Set<String> s = new TreeSet<>(); s.add("bbb"); s.add("aaa"); s.add("ccc"); s.add("abc"); s.add("cba"); for(String str:s){ System.out.println(str); }
Set<Student> stuSet = new TreeSet<>(); Student s1 = new Student(4,"aaa",20); Student s2 = new Student(2,"bbb",30); Student s3 = new Student(3,"ccc",40); Student s4 = new Student(4,"cba",50);
stuSet.add(s1); stuSet.add(s2); stuSet.add(s3); stuSet.add(s4);
for(Student stu:stuSet){ System.out.println(stu); } Set<Teacher> teacherSet = new TreeSet<>(new TeacherCaiPan()); Teacher t1 = new Teacher(1,"aaa",20); Teacher t2 = new Teacher(2,"bbb",30); Teacher t3 = new Teacher(3,"ccc",40); Teacher t4 = new Teacher(4,"cba",20);
teacherSet.add(t1); teacherSet.add(t2); teacherSet.add(t3); teacherSet.add(t4);
for(Teacher t:teacherSet){ System.out.println(t); } } } |
从以上案例可以看出,当存入的是基本数据类型和字符串时,TreeSet有自动升序排列的能力。而当存入的是自定义引用类型,遍历时会抛出异常。因为TreeSet并不知道如何排序。
- 比较器
(1)Comparable接口
TreeSet集合排序的两种方式:
第一种方式让元素自身具备比较性。也就是元素需要实现Comparable接口,重写compareTo 方法。
这种方式也作为元素的自然排序,也可称为默认排序。
compareTo 方法:this和参数的比较。
- 返回整数 this比参数大 (this排在参数后面)
- 返回负数 this比参数小 (this排在参数前面)
- 返回零 this和参数相等(认为是重复数据)
示例代码
class Student implements Comparable<Student>{
private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; }
public Student(){}
public Student(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } @Override public int compareTo(Student stu) { // this 和 参数 stu 比大小 // 返回整数 this > stu (也就是 this排在后面) // 返回负数 this < stu // 返回0 this 等于 stu /*if(this.id>stu.getId()){ return 1; } else if(this.id<stu.getId()){ return -1; } else{ return 0; }*/
//return this.id-stu.getId(); //return stu.getId()-this.id; //return this.name.compareTo(stu.getName()); int x = this.id-stu.getId(); if(x==0){ return this.name.compareTo(stu.getName()); }else{ return x; } } } |
- Comparator接口
当元素自身不具备比较性,或者元素自身具备的比较性不是所需的。
那么这时只能让容器自身具备。
定义一个类实现Comparator 接口,重写compare方法。并将该接口的子类对象作为参数传递给TreeSet集合的构造方法。
当Comparable比较方式,及Comparator比较方式同时存在,以Comparator
比较方式为主。
compare方法比较规则:
- 返回整数 参数1大于参数2 (参数1排在参数2的后面)
- 返回负数 参数1小于参数2 (参数1排在参数2的前面)
- 返回零 参数1等于参数2 (认为是重复数据)
示例代码
class TeacherCaiPan implements Comparator<Teacher>{
public int compare(Teacher t1, Teacher t2) { // t1>t2 返回整数 // t1<t2 返回负数 // t1==t2 返回0 //return t1.getId()-t2.getId(); return t1.getName().compareTo(t2.getName()); } }
class Teacher{ private int id; private String name; private int age; public Teacher(){ }
public Teacher(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } 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; }
public String toString() { return "Teacher [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
Set<Teacher> teacherSet = new TreeSet<>(new TeacherCaiPan()); Teacher t1 = new Teacher(1,"aaa",20); Teacher t2 = new Teacher(2,"bbb",30); Teacher t3 = new Teacher(3,"ccc",40); Teacher t4 = new Teacher(4,"cba",20);
teacherSet.add(t1); teacherSet.add(t2); teacherSet.add(t3); teacherSet.add(t4); |
(3)TreeSet去重复
情况1:
当存入的数据是根据类型本身实现 Comparable接口方式比较大小,判断重复就是根据重写的compareTo方法。如果返回的是0 则认为是重复数据。
示例代码
public class Demo12_TreeSet去重复 { public static void main(String[] args) { Set<Cat> set = new TreeSet<>();
Cat c1 = new Cat(1,"aaa"); Cat c2 = new Cat(2,"bbb"); Cat c3 = new Cat(3,"ccc"); Cat c4 = new Cat(1,"ddd");
set.add(c1); System.out.println(set.add(c2)); System.out.println(set.add(c3)); System.out.println(set.add(c4));
for(Cat c:set){ System.out.println(c); } } }
class Cat implements Comparable<Cat>{
private int id; private String name;
public Cat(){}
public Cat(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } public int compareTo(Cat o) {
return this.id-o.getId(); }
@Override public String toString() { return "Cat [id=" + id + ", name=" + name + "]"; } } |
情况2: 当存入对象本身没有比较大小能力,而是根据比较器比较大小时,根据比较器中的compare方法的返回值来决定是否重复。返回0则认为是重复数据。
示例代码
public class Demo12_TreeSet去重复2 {
public static void main(String[] args) {
Set<Dog> set = new TreeSet<>(new DogComparator());
Dog d1 = new Dog(1,"aaa"); Dog d2 = new Dog(2,"bbb"); Dog d3 = new Dog(3,"ccc"); Dog d4 = new Dog(4,"aaa");
set.add(d1); System.out.println(set.add(d2)); System.out.println(set.add(d3)); System.out.println(set.add(d4));
for(Dog c:set){ System.out.println(c); } } } class DogComparator implements Comparator<Dog>{
public int compare(Dog o1, Dog o2) { //return 0; return o1.getName().compareTo(o2.getName()); } }
class Dog{ private int id; private String name;
public Dog(){}
public Dog(int id, String name) { super(); this.id = id; this.name = name; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String toString() { return "Cat [id=" + id + ", name=" + name + "]"; } } |
7、TreeSet案例
将字符串中的数值进行排序。
例如String str="8 10 15 5 2 7";
使用 TreeSet完成。
思路:
1、将字符串切割。
2、可以将这些对象存入TreeSet集合。
因为TreeSet自身具备排序功能。
public class Demo11_TreeSet练习 {
public static void main(String[] args) {
String str="8 10 15 5 2 7";
Set<Integer> set = new TreeSet<>();
String[] ss = str.split(" +"); for(String s:ss){ set.add(Integer.parseInt(s)); } System.out.println(set); }
} |
四、List接口
List接口是Collection的子接口,实现List接口的容器类中的元素是有顺序的,而且可以重复。
List容器中的元素都对应一个整数型的序号记录其在容器中的位置,可以根据此序号存取元素。
主要实现类有 ArrayList 和 LinkedList。
1、List中的方法
相对Collection接口扩展的方法有:
Object get(int index); Object set(int index,Object element); void add(int index,Object element); Object remove(int index); int indexOf(Object o); int lastIndexOf(Object o); |
示例代码
public class Demo01_ArrayList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//添加数据 list.add("aaa"); list.add("bbb"); list.add("ccc"); list.add("ddd"); list.add("aaa");
//元素个数 System.out.println("list中的元素个数为:"+list.size()); System.out.println(list); // list是有序的(和添加时的顺序相同) for(String s:list){ System.out.println(s); }
//根据索引获取元素 System.out.println("第2个元素为:"+list.get(1)); System.out.println("第6个元素为:"+list.get(5));
for(int i=0;i<list.size();i++){ System.out.println(list.get(i)); }
System.out.println(list.indexOf("aaa")); System.out.println(list.indexOf("bbb")); System.out.println(list.lastIndexOf("bbb")); System.out.println(list.lastIndexOf("aaa"));
list.add(1,"xxx"); //在指定位置添加一个元素,后面的元素依次后移 System.out.println(list);
list.set(1,"yyy"); // 在指定位置添加一个元素,替换原有的元素 System.out.println(list);
list.remove("aaa"); //只会移除第一个aaa System.out.println(list);
list.remove(0); //移除指定位置的元素 System.out.println(list); } } |
删除list中全部元素:
示例代码
for(String str:list){ list.remove(str); // 增强型for循环不允许调用remove方法删除元素 } System.out.println(list);
int size = list.size(); for(int i=0;i<size;i++){ list.remove(list.get(0)); } System.out.println(list);
for(int i=list.size()-1;i>=0;i--){ list.remove(list.get(i)); } System.out.println(list); |
2、ArrayList
--| Iterable ----| Collection ----| List -----| ArrayList 底层采用数组实现,默认10。每次增长 60%,((oldCapacity * 3)/2 + 1) 查询快,增删慢。 ---------| LinkedList |
ArrayList:实现原理:数组实现, 查找快, 增删慢。
数组为什么是查询快?因为数组的内存空间地址是连续的.
ArrayList底层维护了一个Object[] 用于存储对象,默认数组的长度是10。可以通过 new ArrayList(20)显式的指定用于存储对象的数组的长度。
当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来的容量的1.5倍。
由于ArrayList是数组实现, 在增和删的时候会牵扯到数组增容, 以及拷贝元素,所以慢。数组是可以直接按索引查找, 所以查找时较快。
可以考虑,假设向数组的0角标位置添加元素,那么原来的角标位置的元素需要整体往后移,并且数组可能还要增容,一旦增容,就需要将老数组的内容拷贝到新数组中。所以数组的增删的效率是很低的。
3、LinkedList
LinkedList:链表实现, 增删快, 查找慢
由于LinkedList:在内存中的地址不连续,需要让上一个元素记住下一个元素。所以每个元素中保存的有下一个元素的位置。虽然也有角标,但是查找的时候,需要从头往下找,显然是没有数组查找快的。但是,链表在插入新元素的时候,只需要让前一个元素记住新元素,让新元素记住下一个元素就可以了,所以插入很快。
由于链表实现, 增加时只要让前一个元素记住自己就可以, 删除时让前一个元素记住后一个元素, 后一个元素记住前一个元素。这样的增删效率较高。
但查询时需要一个一个的遍历, 所以效率较低。
特有方法:
addFirst(E e) addLast(E e) getFirst() getLast() removeFirst() removeLast() 如果集合中没有元素,获取或者删除元 素抛:NoSuchElementException
|
4、面试常考
ArrayList与LinkedList的区别?
他们都是线性表,但是ArrayList基于数组(顺序存储),LinkedList基于链表(链式存储)
由于实现的不同,ArrayList在随机访问元素时性能较高,插入和删除元素时效率较低,LinkedList则反之。
5、Vector(不常用)
Vector: 描述的是一个线程安全的ArrayList。
ArrayList: 单线程效率高
Vector: 多线程安全的,所以效率低
特有的方法: void addElement(E obj) 在集合末尾添加元素 E elementAt( int index) 返回指定角标的元素 Enumeration elements() 返回集合中的所有元素,封装到Enumeration对象中 Enumeration 接口: boolean hasMoreElements() 测试此枚举是否包含更多的元素。 E nextElement() 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。 |
示例代码
public static void main(String[] args) { Vector v = new Vector(); v.addElement("aaa"); v.addElement("bbb"); v.addElement("ccc"); System.out.println( v ); System.out.println( v.elementAt(2) ); // ccc // 遍历Vector遍历 Enumeration ens = v.elements(); while ( ens.hasMoreElements() ){ System.out.println( ens.nextElement() ); } } |
五、操作集合的工具类 Collections
Collections常用功能
List排序:sort方法
Collection元素搜索:binarySearch方法
改变Collection中的元素:addAll方法
|
示例代码
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>(); list.add(4); list.add(1); list.add(3); list.add(5); list.add(2); /* //Collections.sort(list); Collections.sort(list,new Comparator<Integer>(){ public int compare(Integer o1, Integer o2) { return o2-o1; } }); System.out.println(list);
*/ Collections.shuffle(list); // 乱序 洗牌 System.out.println(list);
Collections.reverse(list); System.out.println(list);
System.out.println(Collections.max(list)); Set<Integer> set = new HashSet<>(); set.add(2); set.add(21); set.add(3); System.out.println(Collections.max(set)); } |
六、栈(Stack)
Stack类是Vector类的子类。特点是先进后出。
常用方法:
boolean | empty() |
E | peek() |
E | pop() |
E | push(E item) |
int | search(Object o) |
示例代码
public class Demo03_Stack { public static void main(String[] args) { Stack<String> st = new Stack<>(); st.push("aaa"); st.push("bbb"); st.push("ccc"); st.push("ddd");
System.out.println(st.peek()); // 获取栈顶元素,但不移除 System.out.println(st.peek()); System.out.println(st.pop()); // 弹出栈顶元素 --- 获得栈顶元素,并将其从栈中移除 System.out.println(st); System.out.println(st.isEmpty()); System.out.println(st.search("aaa")); // 自顶向下找,从1开始数 } } |