集合
什么情况使用集合框架?
如果不知道程序运行时需要多少对象,或者需要更复杂的方式存储对象
比较重要的

学习路线

Collection类

import java.util.ArrayList;
import java.util.Collection;
/*
集合框架:
Collection:存放的是单一值
特点:
1.可以存放不同类型的数据,而数组只能存放同一类型的数据
2.当使用arrayList子类实现的时候,初始长度是10,当长度不够的时候,会进行自动扩容
api方法(增,删,查):
增加数据的方法
add:要求必须传入的参数是Object对象,因此当写入基本是基本类型的时候,进行了装箱和拆箱,只不过过程不可见
addAll:添加另一个集合的元素到此集合中
删除数据的方法
clear:只是清空集合中的元素,但是此集合对象并没有被回收
remove:删除指定元素
removeAll:删除指定集合
retainAll:用于保留 arraylist 中在指定集合中也存在的那些元素,也就是删除指定集合中不存在的那些元素
如果 arraylist 中删除了元素则返回 true。
如果 arraylist 类中存在的元素与指定 collection 的类中元素不兼容,则抛出 ClassCastException 异常。
如果 arraylist 包含 null 元素,并且指定 collection 不允许 null 元素,则抛出 NullPointerException 。
查询数据的方法
contains:判断集合中是否包含指定的元素值
containsAll:判断集合中是否包含另一个集合
isEmpty:判断集合是否为空
size:返回当前集合的大小
集合转数组的操作
toArray:将集合转化成数组
*/
public class CollectionDemo {
public static void main(String[] args) {
//Constructs an empty list with an initial capacity of ten.
Collection collection = new ArrayList();//必须子类实现,接口无法new
collection.add(1);
collection.add(true);
collection.add(1.23);
collection.add("abc");
System.out.println(collection);
((ArrayList)collection).add(0,"java");//强制类型转换
System.out.println(collection);
Collection collection1 = new ArrayList();
collection1.add("a");
collection1.add("b");
collection1.add("c");
collection1.add("d");
collection.addAll(collection1);//将另一个集合全部元素添加进来
System.out.println(collection);
// collection.clear();
// System.out.println(collection);
System.out.println(collection.contains("a"));
System.out.println(collection.containsAll(collection1));
System.out.println(collection.isEmpty());
// collection.remove("a");
// System.out.println(collection);
System.out.println(collection1.retainAll(collection));//false
// System.out.println(collection.retainAll(collection1));
// true 并且collection中只剩下[a, b, c, d],即collection1中所包含的元素
Object[] objects = collection.toArray();
//注意:如果不确定返回的类型,可以使用.var进行补全 collection.toArray().var
collection.add("a");
System.out.println(collection);
//[java, 1, true, 1.23, abc, a, b, c, d, a] 不唯一
}
}
List和Set接口
Collection接口中存储一组不唯一、无序的对象
List接口中存储一组不唯一、有序的对象
Set接口中存储一组唯一、无序的对象
Map接口中存储一组键值对象,提供key到value的映射
1.List接口的实现类
List特点:有序,不唯一
ArrayList 实现了长度可变的数组,在内存中分配连续空间
优点:遍历元素和随机访问元素的效率比较高
缺点:添加和删除需要大量移动元素效率低,按内容查询效率低
import java.util.ArrayList;
import java.util.List;
/*
集合框架:
List
特点:
1.可以存放不同类型的数据,而数组只能存放同一类型的数据
2.当使用arrayList子类实现的时候,初始长度是10,当长度不够的时候,会进行自动扩容
api方法(增,删,查):
继承Collection
get:输出指定下标的元素
indexOf:输出指定元素的下标(从前往后找)
lastIndexOf:输出指定元素的下标(从后往前找)
set:将此列表中指定位置的元素替换为指定元素
subList:截取指定下标元素到新的集合中
jdk9中新增了一个of方法:可以给集合一次性添加多个元素
List of = List.of(1,2,3,4);
结果为:[1,2,3,4]
使用前提:当集合中存储的元素个数已经确定了,不再改变时使用。
注意:
1.of方法只适用于List接口,Set接口,Map接口,不适用于接口的实现类
2.of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常
3.Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常
*/
public class CollectionDemo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add(1);
list.add(true);
list.add("a");
System.out.println(list);
System.out.println(list.get(0));//输出指定下标的元素
System.out.println(list.indexOf("a"));//输出指定元素的下标(从前往后找)
System.out.println(list.lastIndexOf("a"));//输出指定元素的下标(从后往前找)
list.set(0, "java");//将此列表中指定位置的元素替换为指定元素
System.out.println(list);//[a, 1, true, a]->[java, 1, true, a]
List list1 = list.subList(0, 2);//截取指定下标元素到新的集合中
System.out.println(list1);//[java, 1]
}
}
LinkedList采用链表存储方式
优点:插入、删除元素效率比较高
缺点:遍历和随机访问元素效率低下
import java.util.LinkedList;
/*
LinkedList拥有更加丰富的方法实现,需要使用的时候查询API即可,不需要全部记忆
*/
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add(123);
linkedList.add(false);
linkedList.add("abc");
System.out.println(linkedList);
linkedList.add(0, "java");
System.out.println(linkedList);
//LinkedList特有的头插和尾插
linkedList.addFirst("first");
linkedList.addLast("last");
System.out.println(linkedList);
System.out.println(linkedList.element());//获取对应的第一个元素
linkedList.offer(33);//添加一个元素
System.out.println(linkedList);
}
}
2.Vector和ArrayList
**面试经常问区别:
** 1.ArrayList是线程不安全的,效率高;Vector是线程安全的,效率低
2.ArrayList在进行扩容的时候,扩容1.5倍;Vector在扩容的时候,扩容2倍
import java.util.Vector;
/*
1.Vector也是List接口的一个子类实现
2.Vector跟ArrayList一样,底层都是用数组进行实现的
面试经常问区别:
1.ArrayList是线程不安全的,效率高;Vector是线程安全的,效率低
2.ArrayList在进行扩容的时候,扩容1.5倍;Vector在扩容的时候,扩容2倍
*/
public class VectorDemo {
public static void main(String[] args) {
Vector vector = new Vector();
vector.add(1);
vector.add(true);
vector.add("123");
System.out.println(vector);
}
}
练习

import java.util.Objects;
public class Dog {
private String name;
private String color;
public Dog(){
}
public Dog(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dog dog = (Dog) o;
return Objects.equals(name, dog.name) &&
Objects.equals(color, dog.color);
}
@Override
public int hashCode() {
return Objects.hash(name, color);
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
'}';
}
}
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List list = new ArrayList();
Dog d1 = new Dog("大黄","red");
Dog d2 = new Dog("二黄","black");
Dog d3 = new Dog("三黄","green");
list.add(d1);
list.add(d2);
list.add(d3);
System.out.println(list);
//[Dog{name='大黄', color='red'}, Dog{name='二黄', color='black'}, Dog{name='三黄', color='green'}]
System.out.println(list.size());
list.remove(d1);
System.out.println(list);
System.out.println(list.contains(d2));
//扩展
Dog d4 = new Dog("二黄","black");
//此时d4返回什么?
// System.out.println(list.contains(d4));//false 地址不一样
//怎么把结果变成true----->重写equals和hasCode,将对比地址改为对比实际属性值
System.out.println(list.contains(d4));//true
}
}
Iterator接口
Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList和 HashSet等集合。
Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。

迭代器 it 的基本操作是 next 、hasNext 和 remove
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
调用 it.remove() 将迭代器返回的元素删除。
获取一个迭代器
集合想获取一个迭代器可以使用 iterator() 方法:
// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;
public class RunoobTest {
public static void main(String[] args) {
// 创建集合
ArrayList sites = new ArrayList();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
// 获取迭代器
Iterator it = sites.iterator();
// 输出集合中的第一个元素
System.out.println(it.next());//Google
}
}
循环集合元素
让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环
while(it.hasNext()) {
System.out.println(it.next());
}
删除元素
要删除集合中的元素可以使用 remove() 方法。
以下实例我们删除集合中小于 10 的元素:
// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;
public class RunoobTest {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(12);
numbers.add(8);
numbers.add(2);
numbers.add(23);
Iterator<Integer> it = numbers.iterator();
while(it.hasNext()) {
Integer i = it.next();
if(i < 10) {
it.remove(); // 删除小于 10 的元素
}
}
System.out.println(numbers);//[12, 23]
}
}
关于解决使用remove出现的并发操作异常的问题
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
/*
在java代码中包含三种循环的方式
do...while、for、while
还有一种增强for循环的方式,可以简化循环的编写
在所有的集合类,都默认实现了Iterable的接口,实现此接口意味着具备了实现增强for循环的能力
增强for循环本质上是用的也是interator迭代器的功能
方法:
iterator()
forEach()
在iterator()方法中要求返回一个Iterator的接口子类实例化对象
此接口中包含了:
hasNext()
next()
实现了Iterable的接口,当调用它的时候返回一个迭代器iterator(),迭代器iterator()也是一个接口对象,实现出来后用来调用hasNext()和next()
在使用iterator进行迭代的过程中,如果删除其中某个元素会报错,并发操作异常,因此如果遍历的同时需要修改元素,建议使用listIterator()
ListIterator迭代器提供了向前和向后两种迭代方式,始终是通过cursor和lastret的指针来获取元素值及向下的遍历索引
当使用向前遍历的时候,必须要保证指针在迭代器的结尾,否则无法获取结果值
*/
public class IteratorDemo {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
// for (int i=0;i<list.size();i++){
// System.out.println(list.get(i));
// }
//迭代器
// Iterator iterator = list.iterator();
//并发操作异常
// while (iterator.hasNext()){
// Object o = iterator.next();
// if(o.equals(1)){
// list.remove(1);
// }
// System.out.println(o);
ListIterator iterator = list.listIterator();
while (iterator.hasNext()){
Object o = iterator.next();
if(o.equals(1)){
iterator.remove();
}
System.out.println(o);
}
System.out.println("-------------");
while (iterator.hasPrevious()){
System.out.println(iterator.previous());
}
//简便写法
System.out.println("------------------");
for (Object i : list){
System.out.println(i);
}
}
}
Set接口
HashSet采用HashTable哈希表存储结构
优点:添加速度快,查询速度快,删除速度快
缺点:无序
LinkedHashSet
采用哈希表存储结构,同时使用链表维护次序
特点:有序
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/*
1.set中存放的是无序,唯一的数据
2.set不可以通过下标获取对应的元素的值,因为它是无序的
*/
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet();
set.add(123);
set.add("abc");
set.add(true);
System.out.println(set);//[abc, 123, true] 无序
set.add(123);
System.out.println(set);//[abc, 123, true] 唯一
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("--------------------------");
//将while循环改成for循环
for(Iterator iter = set.iterator();iter.hasNext();){
System.out.println(iter.next());
}//推荐使用此种方式
}
}
TreeSet采用二叉树(红黑树)的存储结构
优点:有序(排序后升序),查询速度比List快
缺点:查询速度没有HashSet快
红黑树简介
是一种自平衡二叉树,相比于平衡二叉树(AVL树),红黑树牺牲了部分平衡性以换取插入、删除时少量的旋转操作,整体来说性能要优于AVL树
特点:
1.节点是红色或者黑色
2.根节点是黑色
3.每个叶节点(NIL节点)是黑色
4.从任意节点到其每个叶子的所有路径都包含相同数目的黑色节点
5.每个红色节点的两个子节点都为黑色(从每个叶子节点到根的路径上不能有两个连续的红色节点)
6.最长路径不超过最短路径的两倍
例如:

TreeSet和HashSet
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person(){}
public Person(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 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 Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/*
1.set中存放的是无序,唯一的数据
2.set不可以通过下标获取对应的元素的值,因为它是无序的
3.设置元素的时候,如果是自定义对象,会查找对象中的equals和hashcode的方法,如果没有,比较的是地址
*/
public class SetDemo {
public static void main(String[] args) {
// TreeSet treeSet = new TreeSet();//存储数据结构:红黑树
// treeSet.add(123);
// treeSet.add(2);
// treeSet.add(7);//必须是相同的数据类型
// System.out.println(treeSet.ceiling(3));//返回此集合中最小元素大于或等于给定元素,如果没有此元素,则返回 null 。
// System.out.println(treeSet);
HashSet hashSet = new HashSet();//用hasMap实现
hashSet.add(new Person("张三",12));
hashSet.add(new Person("张三",12));
hashSet.add(new Person("李四",13));
hashSet.add(new Person("王五",14));
hashSet.add(new Person("赵六",15));
System.out.println(hashSet);
}
}
自建类如何让hashSet判断输入内容是否相同
import java.util.Objects;
/*
为了提高比较的效率,会根据内容算出一个hash值,用hash值来判断是否相同,效率会提升
但hash值是不安全的,hash值一致但内容不一定相同
如果出现hash值相同的情况,那么就必须使用equals()方法来判断内容是否相同
hashCode()添加内容时先用key.hasCode(),计算hash值作比较,如果hash值相等
equals()在调用key.equals(k)比较内容是否相等
这样即保证了效率又保证了安全性
*/
public class Students {
private int no;
private String name;
public Students(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "Students{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
//重写
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Students students = (Students) o;
return no == students.no &&
Objects.equals(name, students.name);
}
@Override
public int hashCode() {
return Objects.hash(no, name);
}
}
import java.util.HashSet;
public class HashSetDemo2 {
public static void main(String[] args) {
Students s1 = new Students(101, "jim1");
Students s2 = new Students(102, "jim2");
Students s3 = new Students(103, "jim3");
Students s4 = new Students(101, "jim1");
Students s5 = new Students(105, "jim5");
HashSet<Students> set = new HashSet<>();
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
System.out.println(set);//输出5个
//面试重点:
//[Students{no=103, name='jim3'}, Students{no=105, name='jim5'}, Students{no=102, name='jim2'}, Students{no=101, name='jim1'}, Students{no=101, name='jim1'}]
//为什么?
//因为Students并没有默认的equals()和hasCode()方法,因此去调用Object类中的
//Object中的hasCode()
//public native int hasCode():值是对象地址,对象地址是相同的
//Object中的equals()
//默认比较对象地址
//解决方法:在自己的类中重写
//输出4个
//[Students{no=101, name='jim1'}, Students{no=102, name='jim2'}, Students{no=103, name='jim3'}, Students{no=105, name='jim5'}]
}
}
Comparable接口
import java.util.Objects;
public class Person implements Comparable{
private String name;
private int age;
public Person(){}
public Person(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 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 Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/**
* 内部比较器
* 此比较器按照age的长度进行比较
* @param o
* @return
*/
@Override
public int compareTo(Object o) {
Person p = (Person)o;
if(p.age>this.age){
return 1;
}else if(p.age<this.age){
return -1;
}else return 0;
}
}
import java.util.*;
/*
树中的元素是要默认进行排序操作的,如果是基本类型的话,自动比较,如果是引用类型的话,需要自己自定义比较器
比较器分类:
内部比较器:定义在元素的类中,通过实现comparable接口来实现
外部比较器:定义在当前类中,通过Comparator接口来实现,但是要将该比较器传到集合中
外部比较器可以定义成一个工具类,此时所有需要比较的规则如果一致的话,可以复用,而内部比较器只有在存储当前对象的时候才可以使用
如果两者同时存在,使用外部比较器
*/
public class SetDemo implements Comparator<Person> {//外部比较器
public static void main(String[] args) {
//存储数据结构:红黑树
TreeSet treeSet = new TreeSet(new SetDemo());//new SetDemo() 将外部比较器传入
// treeSet.add(123);
// treeSet.add(2);
// treeSet.add(7);//必须是相同的数据类型
// System.out.println(treeSet.ceiling(3));//返回此集合中最小元素大于或等于给定元素,如果没有此元素,则返回 null 。
// System.out.println(treeSet);
treeSet.add(new Person("张三",12));
treeSet.add(new Person("张三",12));
treeSet.add(new Person("李四",13));
treeSet.add(new Person("王五",14));
treeSet.add(new Person("赵六",15));
System.out.println(treeSet);//运行报错,原因是运行的时候无法排序
//解决方法,实现一个比较器Comparable(内部比较器)
//[Person{name='赵六', age=15}, Person{name='王五', age=14}, Person{name='李四', age=13}, Person{name='张三', age=12}]
// HashSet hashSet = new HashSet();
// hashSet.add(new Person("张三",12));
// hashSet.add(new Person("张三",12));
// hashSet.add(new Person("李四",13));
// hashSet.add(new Person("王五",14));
// hashSet.add(new Person("赵六",15));
// System.out.println(hashSet);
}
@Override
public int compare(Person o1, Person o2) {
if(o1.getAge()>o2.getAge()){
return 1;
}else if(o1.getAge()<o2.getAge()){
return -1;
}else return 0;
}
}
泛型
基本概念
import java.util.ArrayList;
import java.util.List;
/*
当做一些集合的统一操作的时候,需要保证集合的类型是统一的,此时需要泛型来进行控制
优点:
1.数据安全
2.获取数据时效率较高
因此给集合中的元素设置相同的类型就是泛型的基本需求
使用:
在定义对象的时候,通过<>中设置合理的类型来进行实现
*/
public class FanXingDemo {
public static void main(String[] args) {
// List list = new ArrayList();
// list.add(1);//new Integer
// list.add("abc");//new String("abc")
// list.add(true);//new Boolean(true)
// list.add(new Person("zhangsan",15));
// System.out.println(list);
List<String> list = new ArrayList<String>();
list.add("1");//new Integer
list.add("abc");//new String("abc")
list.add("true");//new Boolean(true)
list.add(new Person("zhangsan",15).toString());
//普通for循环
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
System.out.println("---------------------");
//增强for循环
// for(Object iter : list){
// System.out.println(iter);
// }
for(String iter : list){
System.out.println(iter);
}
}
}
泛型的高阶运用
1.泛型类
public class FanXingClass<A> {//A仅仅起到占位作用
private int id;
private A a;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public void show(){
System.out.println("id:"+id+" ,A:"+a);
}
}
import java.util.ArrayList;
import java.util.List;
/*
泛型的高阶运用
1.泛型类
在定义类的时候在类名后面添加<E,K,V......>,起到占位的作用,方法类中的方法的返回值和属性的类型都可以使用
*/
public class FanXingDemo {
public static void main(String[] args) {
//1.泛型类
FanXingClass<String> fxc = new FanXingClass<String>();
fxc.setA("java");
fxc.setId(1);
fxc.show();
FanXingClass<Integer> fxc2 = new FanXingClass<Integer>();
fxc2.setA(34);
fxc2.setId(2);
fxc2.show();
FanXingClass<Person> fxc3 = new FanXingClass<Person>();
fxc3.setA(new Person("AAA",123));
fxc3.setId(3);
fxc3.show();
}
}
2.泛型接口
第一种实现方式
public interface FanXingInterface<B> {
public B test();
public void test2(B b);
}
public class FanXingInterfaceSub<B> implements FanXingInterface<B>{//仍不指定子类类型
@Override
public B test() {
return null;
}
@Override
public void test2(B b) {
System.out.println(b);
}
}
import java.util.ArrayList;
import java.util.List;
/*
泛型的高阶运用
2.泛型接口
在定义接口的时候在类名后面添加<E,K,V......>
第一种实现方式:子类在进行实现的时候,可以不填写泛型的类型,此时在创建具体的子类对象的时候才决定使用哪种类型
*/
public class FanXingDemo {
public static void main(String[] args) {
FanXingInterfaceSub<String> fxi = new FanXingInterfaceSub<String>();
fxi.test2("abc");
}
}
第二种实现
public interface FanXingInterface<B> {
public B test();
public void test2(B b);
}
public class FanXingInterfaceSub implements FanXingInterface<String>{//直接指定父类类型
//实现String方法
@Override
public String test() {
return null;
}
@Override
public void test2(String s) {
System.out.println(s);
}
}
import java.util.ArrayList;
import java.util.List;
/*
子类在实现泛型接口的时候,只在实现父类的接口的时候指定父类的泛型类型
此时具体方法中的泛型类型必须要跟父类中的保持一致,也无需声明
*/
public class FanXingDemo {
public static void main(String[] args) {
FanXingInterfaceSub fxi = new FanXingInterfaceSub();//无需用<>声明,此时指定String类型
fxi.test2("abc");//abc
}
}
3.泛型方法
public class FanXingMethod<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public<Q> void show(Q q){
System.out.println(q);
System.out.println(t);
}
}
import java.util.ArrayList;
import java.util.List;
/*
泛型方法
定义方法的时候,指定方法的返回值和参数是自定义的占位符,可以使类名中的T,也可以是自定义的Q
只不过在使用Q的时候,需要把<Q>定义在返回值的前面:
public<Q> void show(Q q){
System.out.println(q);
}
*/
public class FanXingDemo {
public static void main(String[] args) {
FanXingMethod<String> fxm = new FanXingMethod<>();
fxm.setT("ttt");
fxm.show(123);
}
}
4.泛型的上限、反省的下限(基本不会使用)
/*
泛型的上限
如果父类确定了,所有的子类可以直接使用
泛型的下限
如果子类确定了,子类的所有父类都可以直接传递参数使用
*/
Map
Map基本概念
特点:key-value映射
HashMap
Key无序 唯一(Set)
Value无序 不唯一(Collection)
LinkedHashMap
有序的HashMap速度快
TreeMap
有序 速度没有hash快
package Map;
/*
map存储的是k-v键值对映射的数据
实现子类:
HashMap:数据+链表(1.7)、数据结构+链表+红黑树(1.8之后)
LinkedHashMap:链表
TreeMap:红黑树
基本api操作:
增加:
put(k,v):插入
查找:
isEmpty:判断是否非空
size:返回map的大小
containsKey:查找k
containsValue:查找v
get:查找对应k的下标
删除:
clear:清空集合中的所有元素
remove:删除指定元素
map.entry:表示的是k-v组合的一组映射关系,k-v成组出现
*/
import java.util.*;
public class MapDemo {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
map.put("d", 4);
System.out.println(map);
System.out.println(map.isEmpty());
System.out.println(map.size());
// map.clear();
System.out.println(map.containsKey("b"));
System.out.println(map.containsValue(4));
System.out.println(map.get("a"));
// map.remove("a");
System.out.println("---------------------------------------------");
//遍历操作
Set<String> keys = map.keySet();
for(String key : keys){
System.out.println(key+" = "+map.get(key));
}
Collection<Integer> values = map.values();//只能根据k获取对应的v值,无法用v获取k
for(Integer i : values){
System.out.println(i);
}
System.out.println("---------------------------------------------");
//迭代器
Set<String> keys2 = map.keySet();
Iterator<String> iterator = keys2.iterator();
while (iterator.hasNext()){
String key = iterator.next();
System.out.println(key+" = "+map.get(key));
}
System.out.println("---------------------------------------------");
Set<Map.Entry<String, Integer>> entries = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator1 = entries.iterator();
while (iterator1.hasNext()){
Map.Entry<String, Integer> next = iterator1.next();
System.out.println(next.getKey()+" = "+next.getValue());
}
}
}
hashMap和HashTable的区别
1.hashMap线程不安全,但效率比较高;hashTable线程安全,但效率较低
2.hashMap中的k,v都可以空(null),但hashTable不允许
为什么hashMap初始值为2的N次幂?
1.方便进行操作,提高效率,用“&”进行运算的效率比直接取模要高
2.在扩容之后涉及到元素的迁移过程,迁移的时候只需要判断二进制的前一位是0还是1
如果是0,表示新数组和旧数组的下标位置保持不变;
如果是1,只需要将索引位置加上旧的数组的长度值即为新数组的下标
【面经】HashMap与ConcurrentHashMap在Jdk1.7和1.8的区别
1.HashMap
在jdk1.7之前HashMap是基于数组和链表实现的,而且采用头插法。
而jdk1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。采用尾插法。
HashMap默认的初始化大小为 16。当HashMap中的元素个数之和大于负载因子*当前容量的时候就要进行扩充,容量变为原来的 2 倍。(这里注意不是数组中的个数,而且数组中和链/树中的所有元素个数之和!)
2.ConcurrentHashMap
在jdk1.7是 分段的数组+链表 ,jdk1.8的时候跟HashMap1.8的时候一样都是基于数组+链表/红黑树。
ConcurrentHashMap是线程安全的
(1)在jdk1.7的时候是使用分段所segment,每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
(2)在jdk1.8的时候摒弃了 Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。synchronized只锁定当前链表或红黑二叉树的首节点。
Collections工具类
package Collection;
/*
Collections工具类
addAll:批量添加
sort:排序
binarySearch:二分查找
fill:替换
shuffle:随机排序
reverse:逆序
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionsDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aau");
list.add("bcuhue");
list.add("caoaiw");
list.add("dcpcowncwk");
Collections.addAll(list, "e","faca","gacscawacavaaw");
System.out.println(list);
list.sort(new Comparator<String>() {//匿名内部类
@Override
public int compare(String o1, String o2) {
if(o1.length()>o2.length()){
return 1;
}else if(o1.length()<o2.length()){
return -1;
}else
return 0;
}
});
System.out.println(list);
//在使用二分查找的侍候需要先进行排序操作,如果数据是无序的,则找不到指定元素
Collections.sort(list);//先排序
System.out.println(list);
System.out.println(Collections.binarySearch(list, "e"));
Collections.reverse(list);
System.out.println(list);
Collections.fill(list, "java");
System.out.println(list);//[java, java, java, java, java, java, java]
}
}
Arrays工具类
package Collection;
import java.util.Arrays;
import java.util.List;
/*
Arrays提供了数组操作的工具类,包含很多方法,比较重要的是:
集合和数组之间的转化
*/
public class ArraysDemo {
public static void main(String[] args) {
//数组转成list
int[] array = new int[]{1,2,3,4};
List<int[]> ints = Arrays.asList(array);//转为一个数组元素
List<Integer> integers = Arrays.asList(1, 2, 3, 4);//转为单个元素
//list转成数组
Object[] objects = ints.toArray();
}
}
集合总结


本文详细介绍了Java集合框架的各种组件,包括Collection、List、Set、Map等接口及其典型实现类的特性与应用场景,同时还深入探讨了泛型、迭代器、常用工具类的使用方法。
16万+

被折叠的 条评论
为什么被折叠?



