一 List详解
1 ArrayList与LinkedList对⽐(会)
相同点:
都是List集合的常⽤的实现类。
对集合中的元素操作的⽅法基本⼀致。
都是线程不安全的
不同点:
ArrayList底层实现是数组, 使⽤数组这种数据结构进⾏数据的存储。
LinkedList底层实现是双链表, 使⽤双链表这种数据结构进⾏数据的存储。
数组与链表结果特点⽐较:
数组实现功能时查找快,添加删除慢
链表查找慢,添加删除快
ArrayList与LinkedList的使⽤场景:
如果对集合中的元素, 增删操作不怎么频繁, 查询操作⽐较频繁。 使⽤ArrayList。
如果对集合中的元素, 增删操作⽐较频繁, 查询操作不怎么频繁。 使⽤LinkedList。
2 . 集合对⾃定义对象的存储
类分成系统类和⾃定义类
系统类往往已经重写了tostring(),equals(),hashcode()等⽅法
⾃定义类,为了实现我们的功能,往往我们需要重写⽗类的常⽤⽅法,⽐如:tostring(),equals(),hashcode()
示例代码:
public class Demo6 {
public static void main(String[] args) {
test1();
}
public static void test1(){
ArrayList list1 = new ArrayList();
list1.add("java");
list1.add("python");
list1.add("python");
list1.add("iOS");
list1.add("bigdata");
System.out.println(list1);
//创建临时集合
ArrayList list2 = new ArrayList();
Iterator i = list1.iterator();
while (i.hasNext()){
Object o = i.next();
if (!(o instanceof String)){
throw new ClassCastException();
}
//向下转型
String s = (String)o;
if (!list2.contains(s)){
//当list1中不包换object时,将它添加进来
/*
* 如果判断成⽴,说明list1中不包含当前的元素
* ⼯作原理:当添加元素时,会让当前的元素与集合中已有的元
素通过equels⽅法进⾏⼀⼀⽐较.过程中
* 只要有⼀次返回true,停⽌⽐较.让整个的contains⽅法返回true.只有所有的⽐较都返回false,最终
* 才会返回false
*
* 实例:添加第三个元素的时候,调⽤equals⽅法的过程
* 第三元素.equals("java") = false 第三元
素.equals("python") = true 停⽌⽐较
*/
list2.add(s);
}
}
System.out.println(list2);
}
二 泛型
1 . 泛型在类中的使⽤(会)
语法部分
定义: 在类名的后⾯, 紧跟上⼀对尖括号
class Animal <T> {}
class Dog <T, M> {}
泛型类的使⽤: 声明引⽤、实例化对象、被继承。
// 1. 声明引⽤
Animal<String> animal;
// 2. 实例化对象
Animal<Integer> animal = new Animal<>();
// 3. 被继承
class Dog extends Animal<String> {}
class Dog<T> extends Animal<T> {}
2 . 泛型类的特点
在类中定义的泛型, 虽然还不明确是什么类型, 但是在当前类中是可以使⽤的。
在使⽤到这个类的时候, 必须要指定泛型的类型。 如果不指定, 默认是Object。
泛型, 只能在当前的类中使⽤, 不能在其他的类中使⽤, 包括⼦类。
示例代码L:
public class Test {
public static void main(String[] args) {
// 实例化⼀个对象
// 指定了泛型的类型是 String 类型
Person<String> xiaoming = new Person<>();
// 泛型,是在编译前期进⾏的类型检查。⼀旦编译完成,泛型就不存在
了。
xiaoming.part = "xiaoming";
Person<Integer> xiaohong = new Person<>();
xiaohong.part = 2;
// 实例化⼀个⼦类对象
Student student = new Student();
student.part = "abc";
// 实例化⼀个泛型⼦类对象
Teacher<Integer> xiaowang = new Teacher<>();
xiaowang.part = 132;
// 如果多个泛型,使⽤这个类的时候,逐个进⾏类型指派即可
Animal<String, Integer> animal = new Animal<>();
// 如果对于⼀个泛型类,在使⽤的时候,没有指派类型,默认是 Object
类型
Person xiaobai = new Person();
}
}
class Animal<T, M> { }
class Person<T> {
String name;
int age;
//在类上确定的泛型可以直接在类的内部使⽤
T part; // 虽然现在还不明确T是什么类型,但是我们可以使⽤这个类
型。
}
class Student extends Person<T> {}
class Teacher<T> extends Person<T> { }
3 .⼦类使⽤接⼝泛型
这⾥有两种情况:
第⼀种:⼦类与接⼝泛型⼀致,接⼝可以直接使⽤泛型类型
第⼆种:接⼝使⽤泛型,⼦类不⽤,在实现的接⼝位置必须指定⼀个具体的数据类型
示例代码:
public class Demo9 {
public static void main(String[] args) {
Bird<String> bird = new Bird();
bird.show("haha");
}
}
interface Inter<E>{
public void show(E e);
}
//1.⼦类与接⼝⼀致
/* 类上的泛型确定了,接⼝上的泛型就确定了,⽅法上的泛型就确定了
*/
class Bird<E> implements Inter<E>{
@Override
public void show(E e) {
}
}
//2.接⼝使⽤泛型,⼦类不⽤
/*在实现的接⼝位置必须指定⼀个具体的泛型
*
* ⽅法使⽤泛型的情况:
* 1.如果是重写的⽅法,泛型与接⼝⼀致
* 2.如果是⼦类⾃⼰的⽅法,可以与接⼝⼀致,也可以有⾃⼰的泛型
*/
class Cat implements Inter<String>{
@Override
public void show(String s) {
}
}
class Pig implements Comparable<Pig>{
@Override
public int compareTo(Pig o) {
return 0;
}
}
class ComWithA implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
ArrayList list = null;
return 0;
}
}
4 . 泛型在⽅法中的使⽤(会)
语法部分
定义: 泛型⽅法中, 在定义⽅法时,在返回值前⾯通过定义泛型。
public static <T> void test(T t) {
}
在定义处的<>⽤来定义泛型
在调⽤处的<>⽤来使⽤泛型
5 泛型⽅法的分类
第⼀:⽅法上的泛型与类上的⼀致
第⼆:⽅法上独⽴使⽤泛型
在⽅法中定义的泛型, 虽然还不明确是什么类型, 但是在当前⽅法中是可以使⽤的。
泛型⽅法, 在使⽤的时候, 不能跟类、接⼝似的, ⼿动的设置类型。 泛型⽅法中, 泛型的设置, 在参数中体现。
泛型⽅法, ⼀定需要是有参的。 参数列表中, 必须有泛型类型。
泛型⽅法中的泛型的设置, 是通过在调⽤⽅法的时候, 实参的类型推导出来的。
泛型, 只能在当前的⽅法中使⽤, 不能在其他的⽅法中使⽤。
三 Collections⼯具类(会)
示例代码:
public class CollectionsUsage {
public static void main(String[] args) {
// 1. 实例化⼀个List集合对象
List<Integer> list = new ArrayList<>();
// 2. 添加元素
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
// 3. 获取⼀个集合中的最⼤值,⼤⼩⽐较通过元素对应的类实现的
Comparable接⼝进⾏⽐较
Integer max = Collections.max(list);
// 获取⼀个集合中的最⼤值,⼤⼩⽐较通过第⼆个参数 Comparator
Integer max2 = Collections.max(list, (i1, i2) -> i2 -
i1);
// 4. 获取⼀个集合中的最⼩值,⼤⼩⽐较通过元素对应的类实现的
Comparable接⼝进⾏⽐较
Integer min = Collections.min(list);
// 获取⼀个集合中的最⼩值,⼤⼩⽐较通过第⼆个参数 Comparator
Integer min2 = Collections.min(list, (i1, i2) -> i2 -
i1);
// 5. 将List集合中的数据进⾏随机的排列(洗牌)
Collections.shuffle(list);
// 6. 交换⼀个List集合中两个下标对应的元素
Collections.swap(list, 0, 2);
// 7. 将⼀个List集合中的元素倒序排列
Collections.reverse(list);
// 8. 将⼀个List集合进⾏排序,元素的⼤⼩⽐较规则使⽤元素对应的类
实现的Comparable接⼝进⾏⽐较⼤⼩
Collections.sort(list);
// 将⼀个List集合按照指定的规则进⾏升序排序,基本不⽤,List集
合中本身就有这样的排序⽅法
Collections.sort(list, (i1, i2) -> i2 - i1);
// 9. 集合中的元素拷⻉,将作为第⼆个参数的集合中的数据拷⻉到第⼀个
集合中
List<Integer> copy = new ArrayList<>();
Collections.addAll(copy, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Collections.copy(copy, list);
// 10. 使⽤指定的数据,填充⼀个集合
Collections.fill(list, 0);
// 11. 将线程不安全的集合,转成线程安全的集合
// Collections.synchronizedCollection()
// Collections.synchronizedList()
// Collections.synchronizedSet()
// Collections.synchronizedMap()
}
}
四 Set集合
1 Set集合的概念
Set集合中,没有下标的概念。
Set集合,是⼀个去重复的集合。 在Set集合中不会添加重复的元素的!在向⼀个Set集合中添加元素的时候, 会先判断这个元素是否已经存在了。如果存在, 则不再添加。
Set集合中, 数据的存储是⽆序的。
⽆序: 所谓的⽆序, 其实指的是元素的添加顺序和存储顺序是不⼀致的。
⽆序, 并不意味着随机!
Set接⼝, 是继承⾃Collection接⼝的。 Set接⼝中的⽅法, 都是从Collection接⼝中继承下来的, 并没有添加新的⽅法。
2 HashSet与TreeSet的区别
HashSet:底层是哈希表,线程不安全的
TreeSet:底层是⼆叉树,线程不安全的
3 HashSet & LinkedHashSet
去重原理:
解释:是通过调⽤元素的hashCode和equals⽅法实现去重,⾸先调⽤hashCode⽅法,拿到当前对象的哈希码值,去让两个对象的哈希码值进⾏⽐较,如果不同,直接认为是两个对象,不再去调⽤equals,如果相同,再继续调⽤equals⽅法,返回true认为是⼀个对象,返回false认为是两个对象
public class Demo3 {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
//说明Set本身的add⽅法内部实现的去重功能,默认调⽤的是元素的
hashCode和equals⽅法
//String类已经默认重写了hashCode和equals⽅法
set.add("java");
set.add("php");
set.add("bigdata");
set.add("html");
set.add("java");
System.out.println(set);
//⾃⼰制定的⽐较规则:并按照年龄和姓名⽐较,相同则认为是同⼀个⼈
HashSet<Person> set1 = new HashSet<>();
set1.add(new Person("bing",20));
set1.add(new Person("bing1",210));
set1.add(new Person("chenbing",120));
set1.add(new Person("wangbing",207));
set1.add(new Person("bing",20));
System.out.println(set1);
}
}
class Person{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//⾃⼰制定的⽐较规则:并按照年龄和姓名⽐较,相同则认为是同⼀个⼈
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return
false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
//
return name.hashCode()+age*1000;
}
}
4 TreeSet(会)
TreeSet是⼀个Set接⼝的实现类,底层实现是⼆叉树。这样的集合,会对添加进集合的元素进⾏去重的处理。 同时, 这个集合会对添加进⼊的元素进⾏⾃动的升序排序。
Comparable接⼝(默认排序)
如果某⼀个类实现这个接⼝, 表示⾃⼰实现了⼀个可以和⾃⼰的对象进⾏⼤⼩⽐较的规则。 此时, 这个类的对象就可以直接存储进TreeSet集合中了。 因为此时TreeSet集合已经知道了怎么对两个这个类的对象进⾏⼤⼩⽐较。
示例代码:
public class Demo8 {
public static void main(String[] args) {
//将字符串存⼊TreeSet
/*
* TreeSet的add⽅法实现的排序,去重.通过调⽤元素的compareTo⽅法
* String类已经实现了Comparable接⼝,并重写了compareTo⽅法
*/
TreeSet<String> set1 = new TreeSet<>();
set1.add("java");
set1.add("php");
set1.add("php");
set1.add("python");
System.out.println(set1);
//将Person2对象存⼊TreeSet
//要求:⼈的年龄和姓名⽐较,年龄和姓名相同是⼀个⼈
TreeSet<Person2> set2 = new TreeSet<>();
set2.add(new Person2("bing",203));
set2.add(new Person2("bing1",22));
set2.add(new Person2("bing2",24));
set2.add(new Person2("bing",20));
System.out.println(set2);
}
}
class Person2 implements Comparable<Person2>{
String name;
int age;
public Person2(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person2{" +
"name='" + name + '\'' +
", age=" + age +
'}';
//⾃⼰制定⽐较规则:⼈的年龄和姓名⽐较,年龄和姓名相同是⼀个⼈
/* @param o 和this进⾏⽐较的Person对象
* @return ⽐较结果
* > 0 : this > o
* ==0 : this == o
* < 0 : this < o
*/
@Override
public int compareTo(Person2 o) {
//先⽐较年龄
int v = this.age-o.age;
//再⽐较姓名
return v == 0 ? this.name.compareTo(o.name) : v;
}
}
5 Comparator接⼝(⼈⼯排序)
定义:使⽤实现了Comparator接⼝的compare()⽅法的⽐较器对象进⾏⽐较
分析1:有了Comparable,为什么还要有comparator?
原因:
对于⾃定义的类,代码是我们⾃⼰编写的,所有在排序时不管是通过Comparator还
是Comparable,排序规则我们都可以⾃⼰制定,所以最终使⽤那种⽅法没有太⼤的区别
对于系统类,影响⾮常⼤.系统类中的代码我们只能⽤,不能改.这也就意味着系统类内部通过Comparable实现的⽐较规则已经确定了.这时我们想使⽤其他的规则对当前的系统类对象进⾏⽐较,只能使⽤Comparator⾃⼰重新制定⽐较规则
分析2:⼈⼯排序和默认排序那个优先级⾼?
答:⼈⼯排序的优先级⾼于默认排序.
我们可以让TreeSet同时获取到Comparator和Comparable的⽐较⽅法,此时对于系统类来说默认排序是系统⾃带的,通过Comparator实现的⼈⼯排序规则是我们想要的,所以系统必须让⼈⼯排序优先于默认排序,才能正常的使⽤后加的排序规则.
示例代码
//1.⽣成⼀个⽐较器(实现了Comparator接⼝的类的对象)
class ComWithLength implements Comparator<String>{
@Override
public int compare(String s1, String s2) {
int num = s1.length()-s2.length();
return num==0?s1.compareTo(s2):num;
}
}
public class Demo5 {
public static void main(String[] args) {
//2.将⽐较器作⽤域TreeSet
ComWithLength comWithLength = new ComWithLength();
TreeSet<String> set = new TreeSet(comWithLength);
/*
* TreeSet的add⽅法实现的排序,去重.通过调⽤元素的compareTo⽅法
* String类已经实现了Comparable接⼝,并重写了compareTo⽅法
*/
set.add("java");
set.add("php");
set.add("bigdata");
set.add("html");
set.add("java");
}
}
五 Map集合
Map是双列集合的顶级接⼝, 这个接⼝并没有继承⾃Collection接⼝。
在Map中, 更多强调的是⼀层映射关系。 在Map中存储的数据, 是⼀个个的键值对(Key-Value-Pair), 键和值是⼀⼀对应的。
需要注意:
由于Map集合并没有实现Iterable接⼝, 因此这个集合是不能使⽤增强for循环遍历的