知识补充
增强型for循环
增强型for循环 for(类型 变量名:集合/数组){使用变量}
集合遍历使用lambda/forEach更简化,只有引用类型才能使用
for(泛型类型 引用:集合)
{引用指向的就是集合中的每一个元素}
[其中元素类型需要一致,即是范型]
for(数组元素的类型 变量名:数组)
{变量名的值就是数组中的每一个元素}
//增强型for循环 for(类型 变量名:集合/数组){使用变量}
//集合遍历使用lambda/forEach更简化,只有应用在引用
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
//for(泛型类型 引用:集合){引用指向的就是集合中的每一个元素}[其中元素类型需要一致,即是范型]
for (Integer i:list)
System.out.println(i);
char[] c={'A','B','D','E'};
//for(数组元素的类型 变量名:数组){变量名的值就是数组中的每一个元素}
for(char c1:c)
System.out.println(c1);
}
subList() 获取子list
subList() ,获取的子list和原来的list占有相同的存储空间;
List<Integer> list=new ArrayList<>();
//给list赋值0-9
for(int i=0;i<=9;i++)
list.add(i);
System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//截取子串subList()
List<Integer> sublist=list.subList(4,8);
System.out.println(sublist);//[4, 5, 6, 7]
把list中(4,8)的元素扩大10,输出list
方法一
遍历每个元素,先利用sublist.indexOf(i)找到该元素位置;
再对该元素利用set(index,元素)进行修改
for(int i:sublist)
sublist.set(sublist.indexOf(i),i*10 );
System.out.println(list);
//[0, 1, 2, 3, 40, 50, 60, 70, 8, 9]
方法二
循环每一个下标,利用get(i)获取每个元素值,再使用set(i,元素进行修改)
for(int i=0;i<sublist.size();i++)
sublist.set(i,sublist.get(i)*10);
System.out.println(list);
//[0, 1, 2, 3, 40, 50, 60, 70, 8, 9]
删除索引为奇数位的元素
方法一 从后往前减
for(int i=list.size()-1;i>=0;i--)
if(i%2!=0)
list.remove(i);
System.out.println(list);
方法二 遍历interator()
int index=0;
Iterator<Integer> i = list.iterator();
while (i.hasNext()){
i.next();
if(index++%2!=0){
i.remove();
}
}
方法三 利用subList()
for (int i=0;i<list.size();i++){
list.subList(i+1,i+2).clear();
}
System.out.println(list);
方法四
for (int i=0;i<list.size();i++){
if(i+1<list.size()){
list.remove(i+1);
}
}
System.out.println(list);
LinkedList集合
List接口的实现类:ArrayList、LinkedList
ArrayList 动态数组实现,适合随机访问元素[线性表]
LinkedList 链表实现,适合插入与删除元素[双向链表]
不同父类能调用的方法不同
LinkedList<Point> ps=new LinkedList<>();
//ps能调用到LinkedList中所有方法
List<Point> ps1=new ArrayList<>();
//ps1能调用到List中所有方法
Collection<Point> ps2=new LinkedList();
//ps2这个引用只能调用到Collection接口中定义的方法
实现List接口
add(元素) 添加元素
get(index) 根据索引,获取元素
add(index,元素) 插入元素
remove(index)、remove(元素) 根据索引或者元素,删除元素
size() 列表元素个数
iterator()、forEach() 循环遍历输出元素
clear() 清空列表元素
isEmpty() 判断列表是否为null
元素可重复,且有序列表,即按存入顺序存放元素
class Point{
int x,y;
public Point(int x,int y){
this.x=x;
this.y=y;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
public class Demo1List {
public static void main(String[] args) {
List<Point> ps=new ArrayList<>();
//ps能调用到List中所有方法
//add(元素)添加元素
ps.add(new Point(1,1));
ps.add(new Point(2,2));
ps.add(new Point(3,3));
//get(index)获取元素
System.out.println(ps.get(2));//Point{x=3, y=3}
//add(index,元素)插入元素
Point p=new Point(10,10);
ps.add(1,p);
System.out.println(ps);
//[Point{x=1, y=1}, Point{x=10, y=10}, Point{x=2, y=2}, Point{x=3, y=3}]
//元素可重复
ps.add(p);
System.out.println(ps);
//[Point{x=1, y=1}, Point{x=10, y=10}, Point{x=2, y=2}, Point{x=3, y=3}, Point{x=10, y=10}]
//删除最后一个remove(index)、remove(元素)
ps.remove(ps.size()-1);
System.out.println(ps);
//[Point{x=1, y=1}, Point{x=10, y=10}, Point{x=2, y=2}, Point{x=3, y=3}]
//修改元素set(index,元素)
Point temp=ps.get(0);
ps.set(0,ps.get(ps.size()-1));
ps.set(ps.size()-1,temp);
System.out.println(ps);
//[Point{x=3, y=3}, Point{x=10, y=10}, Point{x=2, y=2}, Point{x=1, y=1}]
//循环遍历
//Iterator
Iterator<Point> i = ps.iterator();
while (i.hasNext())
System.out.println(i.next());
//forEach
ps.forEach((o)->{
System.out.println(o);
});
//清空clear()
ps.clear();
System.out.println(ps);//[]
//判空isEmpty()
System.out.println(ps.isEmpty());//true
}
}
实现Queue接口[队列]
LinkedList类实现Deque< E >接口、Deque< E >接口继承Queue< E >接口
LinkedList类实现Deque< E >接口
Deque< E >接口继承Queue< E >接口
Queue<类型> queue=new LinkedList<>();
offer(元素); 元素入队[右入]
pool(); 首元素出队[左出]
peek(); 查找出队元素
public static void main(String[] args) {
Queue<String> queue=new LinkedList<>();
//元素入队 offer(元素)
queue.offer("A");
queue.offer("B");
queue.offer("C");
queue.offer("D");
//查看队列中首元素peek()
System.out.println("队首元素"+queue.peek());//左出 //队首元素A
//循环出队 元素出队poll()
while (queue.peek()!=null)
System.out.println(queue.poll());//出队
//若队列中没有元素即队首为null],若继续出队,则返回为null[
System.out.println(queue.poll());//null
}
当队列中没有元素即队首为null ,若继续出队,则返回为null
//循环出队 元素出队poll()
while (queue.peek()!=null)
System.out.println(queue.poll());//出队
//若队列中没有元素即队首为null],若继续出队,则返回为null[
System.out.println(queue.poll());//null
实现Deque接口 [栈]
Deque<类型> statck=new LinkedList<>();
push(元素) 入栈
pop() 出栈
peek() 寻找出栈元素
public static void main(String[] args) {
Deque<String> statck=new LinkedList<>();
//入栈
statck.push("A");
statck.push("B");
statck.push("C");
statck.push("D");
//寻找栈最先的出栈元素 peek()
System.out.println(statck.peek()); //D
while (statck.peek()!=null)
System.out.print(statck.pop()+" ");//出栈顺序 D C B A
//System.out.println(statck.pop());
//空栈继续出栈[即当出栈元素为null时] java.util.NoSuchElementException
}
当空栈继续出栈[即当出栈元素为null时] ,运行错误
java.util.NoSuchElementException
Comparable< T > 接口
Comparable 是接口,comparaTo(o) 比较大小方法
两个对象比大小,返回类型为int
int i=元素1.comparaTo(元素2)
i>0表示元素1大;
i<0表示元素2大;
i==0,两个元素相等
String类型实现Comparable< String >接口,重写了comparaTo(o) 方法,默认比较对象的字母大小
String s="tome";
String s1="Tome";
//int i=元素1.comparaTo(元素2) 比较大小
//i>0表示元素1大;i<0表示元素2大;i==0,两个元素相等
int i=s.compareTo(s1);//i=32
Collections.sort(list);
Collections 一个工具包,如同Arrays
新建五个Student对象,放入List表中进行排序
public static void main(String[] args) {
Student tom = new Student("tom", 19);
Student jack = new Student("jack", 20);
int a=tom.compareTo(jack);
System.out.println(a>0?"tom大":a<0?"jack大":"一样大");//jack大
List<Student> stu=new ArrayList<>();
stu.add(new Student("1",12));
stu.add(new Student("2",18));
stu.add(new Student("3",13));
stu.add(new Student("4",14));
stu.add(new Student("5",11));
System.out.println(stu);
//[Student{name='1', age=12}, Student{name='2', age=18}, Student{name='3', age=13}, Student{name='4', age=14}, Student{name='5', age=11}]
//方法一
// stu.sort(((o1, o2) ->{
// return o1.compareTo(o2);//本质调用类型中的compareTo()方法
// }));
// System.out.println(stu);//调用类型本身的toString()
//[Student{name='5', age=11}, Student{name='1', age=12}, Student{name='3', age=13}, Student{name='4', age=14}, Student{name='2', age=18}]
//方法二
//方法一等价于方法二
Collections.sort(stu);//默认升序
//Student实现了Comparable接口,重写了comparTo()方法
System.out.println("排序后"+stu);
//排序后[Student{name='5', age=11}, Student{name='1', age=12}, Student{name='3', age=13}, Student{name='4', age=14}, Student{name='2', age=18}]
}
//public interface Comparable<T>{} 范化接口
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name,int age){
this.name=name;
this.age=age;
}
@Override
public int compareTo(Student o) { //Student实现了Comparable接口,重写了comparTo()方法
return this.age-o.age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Comparator < T >接口
对已经重写过重写过comparaTo(o) 方法的类,对comparaTo(o) 方法进行规则重写,自定义比较规则
@FunctionalInterface
public interface Comparator<T> {int compare(T o1, T o2);}
Collections.sort(集合,Comparator[重写规则])
String类型实现Comparable< String >接口,重写了comparaTo(o) 方法,默认比较对象的字母大小
按字符串长度排序 使用Collections.sort(集合,Comparator[重写规则])
默认规则:Collections.sort(集合)
List<String> str=new ArrayList<>();
str.add("hello");str.add("tom");str.add("nice to meet you");str.add("hi");str.add("hanmeimei");
//[hello, tom, nice to meet you, hi, hanmeimei]
Collections.sort(str); //默认按字符大小排序
System.out.println(str);
//[hanmeimei, hello, hi, nice to meet you, tom]
重写规则:Collections.sort(集合,Comparator[重写规则])
自定义排序规则,重写排序规则
//匿名内部类
Collections.sort(str, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
//lambda函数 函数式接口
Collections.sort(str,(o1,o2)->{
return o1.length()-o2.length();
});
System.out.println(str);
//[hi, tom, hello, hanmeimei, nice to meet you]
Set集合
Set不重复的、无序的
Set集合中的元素,不能和顺序的下标对应,无法从Set集合中获取除特定的元素
Set存储不重复的对象集合,set集合中存储的对象
不存在两个对象的引用地址相等;
对象是否重复取决于equals()方法是否重写
Set继承了Collection
HashSet 用hash表实现了Set集合
TreeSet 用排序二叉树实现了Set集合add(元素) 添加元素 在集合中没有顺序,不支持排序
size() 获取元素个数
remove(元素) 删除指定元素
clear() 清空元素
isEmpty() 判断集合是否为空
遍历
1,增强版for(类型 变量:集合)
2,forEach(x->System.out.println(x))
简写 forEach(System.out::println)
Set集合无序
Set无序:add(元素)添加的元素在集合中没有顺序,不支持排序
Set<String> set=new HashSet<>();
//Set无序:添加的元素在集合中没有顺序,不支持排序
set.add("A");
set.add("C");
set.add("D");
set.add("B");
System.out.println(set);//[A, B, C, D]
Set集合不重复
set.add("C");
set.add("D");
set.add("B");
System.out.println(set);//[A, B, C, D]
//其他方法
System.out.println(set.size());
System.out.println(set.remove("A"));
//set.clear();
//System.out.println(set.isEmpty());
Set集合遍历
for(类型 变量:集合)
for(String s:set)
System.out.println(s);
forEach(x->System.out.println(x))
简写 forEach(System.out::println)
//forEach
//一个参数,可省略小括号
//set.forEach(x-> System.out.println(x));
set.forEach(System.out::println);//简写
在集合中存储20个不重复的100以内的整数
Set<Integer> sets=new HashSet<>();
Random random=new Random();
while (sets.size()<20){
sets.add(random.nextInt(100)+1);
}
System.out.println(sets);
给数组去重
//给数组元素去重
int[] arr={1,2,32,3,43,2,1};
Set<Integer> sets2=new HashSet<>();
for (int i:arr)
sets2.add(i);
System.out.println(sets2);
Map集合
Map集合,存储"key-value"键值对;
key为value的索引、通过key查找value;
key不能重复;但key值是否重复,取决于equals();
key值可以是null,但只允许有一个key为null
无序
Map是一个接口,实现类HashMap、ThreeMap
常用get、put
依据key找value
put(key,value) 增加数据
当key值已存在,put(key,value) 则修改该key索引的vaule值
size() 元素个数
containsKey(key) 是否包含某个key值,返回类型boolean类型
containsValue(value) 是否包含某个value值,返回类型boolean类型
get(key) 通过key找value 返回value类型的value值,否则返回value类型的默认值[引用类型:null]
remove(key)根据key删除对应元素集合,返回被删除的value类型的value值,否则返回value类型的默认值[引用类型:null]
keySet() 取出集合中所有的keys值,返回类型为Set集合
values() 取出所有的values值,返回类型为Collection集合
clear() 清空元素
isEmpty()判空
常用方法
put(key,value) 增加 / 修改数据、size()
put(key,value) 增加数据
Map<String,Integer> map=new HashMap<>();
//增加数据 put(key,value)
map.put("语文",80);
map.put("数学",70);
map.put("英语",60);
System.out.println(map);
//{数学=70, 语文=80, 英语=60} //Map 无序
put(已存在key,value) 修改value值;
//修改value值 key已存在时,put(已存在key,value) 修改value值
map.put("语文",90);
System.out.println(map);
//{数学=70, 语文=90, 英语=60}
//元素个数 size()
System.out.println(map.size());//3
containsKey(k)、containValue(v)是否包含
//是否包含某个key值 containsKey(key)
System.out.println(map.containsKey("语文"));//true
//是否包含某个value值 containsValue(value)
System.out.println(map.containsValue(70));//true
get(key)、remove(key)
//get(key) 通过key找value,返回value值;若不存在,返回null
System.out.println(map.get("语文"));//90
System.out.println(map.get("物理"));//null
//根据key删除集合中的元素 返回删除元素的value值;若不存在,返回null
System.out.println(map.remove("语文"));//90
System.out.println(map.remove("物理"));//null
keySet()、values()
//key的Set集合
//key的Set集合,取出所有的keys值
Set<String> keys=map.keySet();
System.out.println(keys);//[数学, 英语]
//value的Collection集合,取出所有的values值
Collection<Integer> values=map.values();
System.out.println(values);//[70, 60]
clear()、isEmpty()
map.clear();
System.out.println(map.isEmpty());//true
key值是否重复,取决于equals
key值是否重复,取决于equals()方法是否被重写;
若未重写,则比较两个对象是否是同一个对象;
若重写,则比较两个对象的属性是否相同
key值可以是null,但只允许有一个key为null
class Person{
String id;
String name;
int age;
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) {
//id 找到Person
Map<String,Person> map=new HashMap<>();
map.put("101",new Person("101","小红",20));
Person person=map.get("101");
person.age=23;
//等价于map.get("101").age=23;
//"101"编号的Person属性age改变
System.out.println(map.get("101"));//Person{id='101', name='小红', age=23}
//通过人找人
//key不能重复,可以是null,但允许有一个key是null 是否重复取决于equals()方法是否重写
Map<Person,Person> map1=new HashMap<>();
map1.put(new Person("101","小红",20),new Person("101","小红",20));
map1.put(new Person("101","小红",20),new Person("101","小红",20));
//虽然属性均一样,但是存放在不同的地址中,且Student类没有重写equasl()方法
map1.put(null,new Person("101","小红",20));
map1.put(null,new Person("101","小红",20));
System.out.println(map1.size());//3
}
遍历
//map遍历
Map<String,Integer> map2=new HashMap<>();
map2.put("语文",80);
map2.put("数学",70);
map2.put("英语",60);
map2.put("物理",70);
map2.put("化学",70);
entrySet()
entrySet()遍历-> 将(key,value)作为一行,存储
Set<Map.Entry<String, Integer>> entries = map2.entrySet();
//方法一:entrySet()遍历->将(key,value)作为一行,存储
for(Map.Entry<String,Integer> entry:entries)
//entry就是一个键值对
System.out.println(entry.getKey()+"----"+entry.getValue());
keySet()
先取出keys集合,再通过key索引value值
Set<String> keys=map2.keySet();
for(String key:keys)
System.out.println(key+"++++"+map2.get(key));
forEach()
void forEach(BiConsumer<? super K, ? super V> action)
public interface BiConsumer<T, U> { void accept(T t, U u);}
map2.forEach((k,v)->{
System.out.println(k+"===="+v);
});
面试题
集合有哪些
collection接口的子类包含:Set接口与List接口
List接口的实现类:ArrayList、LinkedList、Stack、Vector等;
List接口的实现类:有序且数据可重复的集合
Set接口的实现类:HashSet、TreeSet、LinkedHashSet等
Set接口的实现类:无序且数据不重复集合
Map接口的实现类:HashMap、ThreeMap、Properties
Map接口的实现类,无序且键不重复的键值对集合
简述HashMap的实现原理
HashMap基于Hash算法实现的, 我们通过put(key, value)存储数据, get(key)取数据,当传入key的时候,HashMap会根据key.hashCode()计算hash值,根据hash值将value保存再bucket中(以数组的形式保存)。
当我们计算出的hash值相同时,我们称为hash冲突,HashMap的做法是用链表和红黑树存储相同的hash值对应的value。当hash冲突的个数比较少(阈值为8.),就使用链表, 比较多就使用红黑树
了解红黑树
红黑树,本质是一颗二叉搜索树 + 节点颜色限制(红/黑) + 规则约定(最长路径中的节点个数不超过最短路径中节点个数的2倍)
红黑树详细讲解与用法
理解java泛型
java泛型(参数化类型)
泛型是java 1.5引入的新特性,泛型的本质是参数类型化
在类,接口和方法的定义过程中,所操作的数据类型被传入的参数指定
java泛型机制广泛地应用于集合中,所有集合类型都带有泛型
在创建集合对象的时候,指定集合中元素的类型
java编译器根据泛型的类型,对集合中元素进行类型检查,减少运行的时候错误
泛型应用场景
class 类名<泛型> – 泛型代表一种类
相当于是一个参数,类定义的时候,不知道具体的类型, 使用类来创建对象的时候,在指定具体类型.
泛型类
泛型类
class MyDog<D>{
D d;
public MyDog(D d){
this.d = d;
}
}
使用泛型类
public static void main(String[] args) {
MyDog<String> m = new MyDog<>("dd");
System.out.println(m.d);
MyDog<D> k = new MyDog<D>(new Date());
// 编译错误 -- 作为一个类型使用的时候,泛型必须指定为某个具体存在的类型
}
泛型接口
类实现接口的时候, 需要指定具体的类型
泛型接口
interface MyCat<E>{
public E getE();
public void setE(E e);
}
实现泛型接口
class AA implements MyCat<String> {
@Override
public String getE() {
return null;
}
@Override
public void setE(String o) {
}
}
泛型方法
class BB{
// <T> -- 定义方法的发返回值前,代表方法要使用泛型
public <T> void fun1(T t){
}
public <T> T fun2(T t){
return t;
}
public <T> String fun3(T t){
return "hello";
}
}
通配符
<?> 无界通配符不确定类型,可以是任意类型
< ? extends T > ,上边界通配符
?是继承自T的任意子类型 , 遵守只读不写(只能作为返回值类型)
< ? super T > , 下边界通配符
? 是 T的任意父类, 遵守只写不读(只能作为参数 , 作为返回值类下,始终返回Object ,意义不大.)
// <?> 无界通配符,即不确定类型,可以是任意类型
class CC<Z>{
}
// <? extends T> ,上边界通配符, 即?是继承自T的任意子类型 , 遵守只读不写(只能作为返回
值类型)
class DD<T1 extends CC>{
public void get(T1 t){
}
}
// <? super T> , 下边界通配符,即? 是 T的任意父类, 遵守只写不读(只能作为参数 , 作为返回值类下,始终返回Object ,意义不大.)
//class EE1<e super FF>{
//}
public class TPFTest {
public static void main(String[] args) {
// 限制元素是Number或者Number的子类.
List<? extends Number> list1 = new ArrayList<>();
list1.add(null);
// Number的子类有Integer , Double , 编译器没有办法确定add时的子类对象 ,所以添加不成功.
Integer i = 100;
//list1.add(i); 编译错误: ? extend T 上界类型通配符, 只读,不能写. add函数受限制.
Number number = list1.get(0); // 可以取出来使用.
// 限制元素是Intger或其父类.
List<? super Integer> list2 = new ArrayList<>();
list2.add(100);
//Integer object = list2.get(0); // 编译错误 : ? super T 下界类型通配符 ,因为没有办法确定父类是谁 , 只能写,不能读.
// 结论:又要写,又要读,写指定的泛型:
List<Integer> list = new ArrayList<>();
list.add(100);
Integer integer = list.get(0);
}
}