1 Collection集合
1.1 常用集合的体系
提示: 有关Collection中的方法可不止上面这些,其他方法可以自行查看API学习。
注意:这张图只是我们常用的集合有这些,不是说就只有这些集合。
1.2 Collection 常用API
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(Object obj)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中
2 迭代器
Collection集合的遍历方式是全部集合都可以直接使用的,Collection集合的遍历方式有三种:
1)Iterator迭代器。
2)foreach(增强for循环)。
3)JDK 1.8开始之后的新技术Lambda表达式(了解)
1.1 Iterator接口
迭代器遍历集合。
public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的
E next():获取下一个元素值!
boolean hasNext():判断是否有下一个元素,有返回true ,反之。
public static void main(String[] args) {
// 使用多态方式 创建对象
Collection<String> coll = new ArrayList<String>();
// 添加元素到集合
coll.add("串串星人");
coll.add("吐槽星人");
coll.add("汪星人");
//遍历
//使用迭代器 遍历 每个集合对象都有自己的迭代器
Iterator<String> it = coll.iterator();
// 泛型指的是 迭代出 元素的数据类型
while(it.hasNext()){ //判断是否有迭代元素
String s = it.next();//获取迭代出的元素
System.out.println(s);
}
}
tips:
- 在进行集合元素获取时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将 会抛出java.util.NoSuchElementException没有集合元素异常。
- 在进行集合元素获取时,如果添加或移除集合中的元素 , 将无法继续迭代 , 将会抛出ConcurrentModificationException并发修改异常.
2.2 foreach遍历
forEach遍历他可以遍历集合也可以遍历数组:
- foreach(增强for循环)遍历集合。
- foreach是一种遍历形式,可以遍历集合或者数组。
- foreach遍历集合实际上是迭代器遍历的简化写法。
- foreach的优点:是编码简单。 foreach的缺点:没有遍历的索引,无法知道遍历到了哪里。
public static void main(String[] args) {
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("殷素素");
lists.add("周芷若");
System.out.println(lists);
// lists = [赵敏, 小昭, 殷素素, 周芷若]
// list
for (String list : lists) {
System.out.println(list);
}
Double[] scores = {99.9 , 100.0 , 887.2 , 99.0};
for (Double score : scores) {
System.out.println(score);
}
}
JDK 1.8开始之后的新技术Lambda表达式。
public static void main(String[] args) {
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("殷素素");
lists.add("周芷若");
System.out.println(lists);
lists.forEach(s -> {
System.out.println(s);
});
lists.forEach(s -> System.out.println(s) );
lists.forEach(System.out::println);
}
3 数据结构
- 栈
先进后出,后进先出 - 队列
先进先出,后进后出 - 数组
查找元素快,通过索引,增删元素慢,原因需要创建一个新数组操作 - 链表 (单链表,和双链表,java的是单链表)
查找元素慢,增删元素快 - 二叉树
- 二叉查找树/二叉排序树
增删改查的性能都很高,但是会出现瘸子现象 - 平衡二叉树
基于查找二叉树,但是让树不要太高,尽量元素均衡,两个子树节点的差绝对值不超过一 - 红黑树
- 就是平衡的二叉查找树的另一种算法, 增删查改性能都很高
- 红黑树不是高度平衡的,它的平衡是通过"红黑树的特性"
- 红黑树的特性:
- 每一个节点或是红色的,或者是黑色的。
- 根节点必须是黑色
- 每个叶节点(Nil)是黑色的;(如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点)
- 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
- 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
- 在进行元素插入的时候,和之前一样; 每一次插入完毕以后,使用黑色规则进行校验,如果不满足红黑规则,就需要通过变色,左旋和右旋来调整树,使其满足红黑规则;
- 二叉查找树/二叉排序树
4 常用结合介绍
4.1 List 集合
list集合的底层基于数组
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
4.1.1 ArrayList 集合(特点:有序、可重复、有索引)
ArrayList 的底层基于数组,查询快,增删改慢
// 遍历方式比Collection集合多了一种通过索引的方式
public static void main(String[] args) {
List<String> lists = new ArrayList<>();
lists.add("java1");
lists.add("java2");
lists.add("java3");
for(int i = 0 ; i < lists.size() ; i++ ) {
System.out.println(lists.get(i));
}
}
4.1.2Vector集合(特点:与ArrayList相比较是线程安全的)
Vector是线程安全的,但是其比较笨重,看其源码发现其只是在每个方法上 加上 synchronized 关键字,因此虽然其可以保证线程安全,但是效率低下,在实际开发中不推荐使用。
4.1.3 LinkedList 集合(特点:有序、可重复、有索引)
ArrayList 的底层基于链表,查询慢,增删改快
LinkedList是支持双链表,定位前后的元素是非常快的,增删首尾的元素也是最快的
所以LinkedList除了拥有List集合的全部功能还多了很多操作首尾元素的特殊功能:
public void addFirst(E e)
:将指定元素插入此列表的开头。public void addLast(E e)
:将指定元素添加到此列表的结尾。public E getFirst()
:返回此列表的第一个元素。public E getLast()
:返回此列表的最后一个元素。public E removeFirst()
:移除并返回此列表的第一个元素。public E removeLast()
:移除并返回此列表的最后一个元素。public E pop()
:从此列表所表示的堆栈处弹出一个元素。public void push(E e)
:将元素推入此列表所表示的堆栈。
// 遍历方式比Collection集合多了一种通过索引的方式
public static void main(String[] args) {
List<String> lists = new LinkedList<>();
lists.add("java1");
lists.add("java2");
lists.add("java3");
for(int i = 0 ; i < lists.size() ; i++ ) {
System.out.println(lists.get(i));
}
}
4.2 Set 集合 (特点:无序、不重复、无索引)
java.util.HashSet
是Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不能保证不一致)。java.util.HashSet
底层的实现其实是一个java.util.HashMap
支持.
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存储和查找性能。保证元素唯一性的方式依赖于:hashCode
与equals
方法。
4.2.1 HashSet 集合 (特点:无序、不重复、无索引)
public static void main(String[] args) {
//创建 Set集合
HashSet<String> set = new HashSet<String>();
//添加元素
set.add(new String("cba"));
set.add("abc");
set.add("bac");
set.add("cba");
//遍历
for (String name : set) {
System.out.println(name);
}
// 运行结果为:“cba abc bac” 《== HashSet可以去重
}
HashSet如果要让对象去重需要重写equals和hashCode代码如下:
// 只要两个对象内容一样结果就是true
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
sex == student.sex &&
Objects.equals(name, student.name);
}
// 只要两个对象内容一就希望他们的哈希值相同!
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
HashSet给对象去重执行代码
public static void main(String[] args) {
//创建 Set集合
HashSet<String> set = new HashSet<String>();
Student s1 = new Student("学生1", 19 , '男');
Student s2 = new Student("学生1"", 19 , '男');
Student s3 = new Student("学生12", 21 , '男');
//遍历
for (String name : set) {
System.out.println(name);
}
}
《面试题》
Set系列集合添加元素无序的根本原因是因为底层采用了哈希表存储元素。
JDK 1.8之前:哈希表 = 数组 + 链表 + (哈希算法)
JDK 1.8之后:哈希表 = 数组 + 链表 + 红黑树 + (哈希算法)
当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
4.2.2 LinkedHashSet 集合 (特点:有序、不重复、无索引)
LinkedHashSet
底层依然是使用哈希表存储元素的,
但是每个元素都额外带一个链来维护添加顺序!!
不光增删查快,还有序。缺点是多了一个存储顺序的链会占内存空间!!而且不允许重复,无索引。
总结:
如果希望元素可以重复,又有索引,查询要快用ArrayList集合。(用的最多)
如果希望元素可以重复,又有索引,增删要快要用LinkedList集合。(适合查询元素比较少的情况,经常要首尾操作元素的情况)
如果希望增删改查都很快,但是元素不重复以及无序无索引,那么用HashSet集合。
如果希望增删改查都很快且有序,但是元素不重复以及无索引,那么用LinkedHashSet集合。
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("bbc");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
结果:
bbb
aaa
abc
bbc
4.2.3 TreeSet集合 (特点:有序、不重复、无索引)
TreeSet集合自排序的方式:
- 有值特性的元素直接可以升序排序。(浮点型,整型)
- 字符串类型的元素会按照首字符的编号排序。
- 对于自定义的引用数据类型,TreeSet默认无法排序,执行的时候直接报错,因为人家不知道排序规则。
自定义的引用数据类型的排序实现:
对于自定义的引用数据类型,TreeSet默认无法排序
所以我们需要定制排序的大小规则,程序员定义大小规则的方案有2种:
- 直接为对象的类实现比较器规则接口Comparable,重写比较方法(拓展方式)
// 如果程序员认为比较者大于被比较者 返回正数、小于返回负数、等于返回0- 直接为集合设置比较器Comparator对象,重写比较方法(更加灵活)
// 如果程序员认为比较者大于被比较者 返回正数、小于返回负数、等于返回0
注意:如果类和集合都带有比较规则,优先使用集合自带的比较规则。
// TreeSet 引用数据类型自定义比较需要用到的类
public class Apple implements Comparable { // 引用数据类型自定义比较需要实现接口Comparable
private String name;
private double price;
private double weight;
private String color;
......
// 重写的比较规则类的比较方法。
// 程序员可以在这里指定比较规则
// 如果程序员认为比较者大于被比较者 返回正数!
// 如果程序员认为比较者小于被比较者 返回负数!
// 如果程序员认为比较者等于被比较者 返回0!
// o1.compareTo(o2)
// 比较者: this
// 被比较者:o
@Override
public int compareTo(Object o) {
Apple o2 = (Apple) o;
if (this.price > o2.price) return 1;
// if (this.price == o2.price) return 0; 有多个相等会去重,不想去重可以直接返回-1或拿其他属性继续比较
return -1
// 建议精度比较实用:Double.compare 最准确方式
// 第一个价格大于第二个价格自然返回正数
// 第一个价格小于第二个价格自然返回负数
// 第一个价格等于第二个价格自然返回0
return Double.compare(this.price , o2.price);
}
}
public static void main(String[] args) {
// TreeSet : 排序不重复集合。
Set<Double> scores = new TreeSet<>();
scores.add(100.0);
scores.add(99.9);
scores.add(69.5);
scores.add(0.1);
scores.add(89.3);
System.out.println(scores);
Set<String> names = new TreeSet<>();
names.add("angel");
names.add("播仔");
names.add("baby");
names.add("tony");
names.add("BoNiu");
names.add("lucy");
names.add("jacky");
names.add("lucky");
System.out.println(names);
// 1.排序不重复集合!
// 排序方式一:让Apple类实现比较规则Comparable ==== 优先级低
Set<Apple> apples = new TreeSet<>();
apples.add(new Apple("阿克苏",39.0,305.5 , "红色"));
apples.add(new Apple("红富士",43.5,604.3 , "粉红色"));
apples.add(new Apple("青苹果",41.3,304.3 , "绿色"));
System.out.println(apples);
// 排序方式二:让集合对象自带比较器Comparator,再定制大小规则。 ==== 优先级高
// public TreeSet(Comparator<? super E> comparator) :比较规则!
// 注意:如果类存在比较规则,而集合自带比较器,优先使用比较器排序!!
// 但是实际开发我们要么给类做比较规则,要么集合自带比较器,二者选其一即可!
Set<Apple> apples1 = new TreeSet<>(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
// 如果程序员认为比较者大于被比较者 返回正数!
// 如果程序员认为比较者小于被比较者 返回负数!
// 如果程序员认为比较者等于被比较者 返回0!
// 集合后续会自动两两送入苹果对象给我们比较!
return Double.compare(o1.getWeight() , o1.getWeight());
}
});
apples1.add(new Apple("阿克苏",39.0,305.5 , "红色"));
apples1.add(new Apple("红富士",43.5,604.3 , "粉红色"));
apples1.add(new Apple("青苹果",41.3,304.3 , "绿色"));
System.out.println(apples1);
}
5 Collections类
java.utils.Collections
是集合工具类,用来对集合进行操作, 常用方法如下:public static void shuffle(List<?> list)
:打乱集合顺序。public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。
5.1 Collections类基本使用
// 基本数据类型排序
public static void main(String[] args) {
// 1.给集合批量添加元素
Collection<String> names = new ArrayList<>();
/**
* 参数一:被添加元素的集合。
* 参数二:可变参数,一批元素。
*/
Collections.addAll(names , "王宝强","贾乃亮","陈羽凡");
System.out.println(names);
// 2.打乱集合的顺序:public static void shuffle(List<?> list)
// 注意:只能打乱有序的List集合。
List<String> lists = new ArrayList<>();
Collections.addAll(lists , "王宝强","贾乃亮", "隔壁老王" , "陈羽凡","陈羽凡");
System.out.println(lists);
Collections.shuffle(lists);// 只能打乱有序的List集合。
System.out.println(lists);
// 3.public static <T> void sort(List<T> list):给List集合升序排序。
List<Integer> lists1 = new ArrayList<>();
Collections.addAll(lists1 , 32 ,21 ,19 ,34 , 98 , 87);
Collections.sort(lists1); // 升序排序!
System.out.println(lists1);
}
5.2 Collections类引用数据类型排序
// Collections工具类 sort方法比较引用数据类型需要用到的类。
public class Orange implements Comparable{
private String name;
private double weight;
private String price;
.........
// o1.compareTo(o2) 比较者:this 被比较者:o
@Override
public int compareTo(Object o) {
Orange o2 = (Orange) o;
// 集合会自动送入两个句子对象来比较!! => 认为o1 > o2返回正整数、认为o1 = o2返回0、认为o1 < o2返回负整数。
return Double.compare(this.weight , o2.weight);
}
}
// 应用数据类型排序
public static void main(String[] args) {
// 1.自定义类型如何排序!
List<Orange> oranges = new ArrayList<>();
Orange o1 = new Orange("红橘子", 654.0, "贼便宜~");
Orange o2 = new Orange("黄橘子", 454.0, "贼便宜~");
Orange o3 = new Orange("黄橘子", 454.0, "贼便宜~");
Orange o4 = new Orange("青橘子", 456.0, "贼便宜~");
Collections.addAll(oranges, o1, o2, o3, o4);
Collections.sort(oranges); // 排序。用类的比较规则排序!
System.out.println(oranges);
// 2.方式二:让sort方法自带比较器
List<Orange> oranges1 = new ArrayList<>();
Orange oo1 = new Orange("红橘子", 654.0, "贼便宜~");
Orange oo2 = new Orange("黄橘子", 454.0, "贼便宜~");
Orange oo3 = new Orange("黄橘子", 454.0, "贼便宜~");
Orange oo4 = new Orange("青橘子", 456.0, "贼便宜~");
Collections.addAll(oranges1, oo1, oo2, oo3, oo4);
Collections.sort(oranges1, new Comparator<Orange>() {
@Override
public int compare(Orange o1, Orange o2) {
// 如果认为o1>o2返回正数
// 如果认为o1<o2返回负数
// 如果认为o1==o2返回0
// 按照其重量进行排序!
// o1 o2 :升序排序
// o2 o1 :降序排序
return Double.compare(o2.getWeight() , o1.getWeight());
}
}); // 排序。用类的比较规则排序!
System.out.println(oranges1);
}