集合
虚线是实现,实现是继承
1. Collection接口
①. 为什么集合使用什么样的泛型,迭代器就用什么泛型 ? [ 理解 ]
②.迭代器用什么泛型,next()方法就用什么泛型 [ 理解 ]
1>. Collection、Map 接口的概述
是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
JDK不提供此接口的任何直接实现,它提供更具体的子接口 (如 Set 和 List)实现
set:元素无序、不可重复的集合- - 类似高中集合
List:元素有序,可重复的集合- - "动态"数组
Map接口:具有映射关系"key-value"对的集合 - - 类似于高中的函数 y=f(x)(x1,y1)(x2,y2)
2>.Collection中常用的方法
boolean add(E e):添加元素[ 注意这里的E是泛型类型 ]
boolean addAll(Collection<? extends E> c):将形成中包含的c所有元素添加到当前集合中 [ 向下限定, 固定上边界 ]
boolean remove(Object obj):从集合中移除指定的元素[ 重写了equals方法]
void clear():清除集合中的元素
boolean contains(Object o):判断集合中是否存在指定的元素[ 重写了equals方法]
boolean isEmpty():判断集合是否为空
int size():集合的长度,也就是集合中元素的个数
Iterator<E>iterator( ):返回一个Iterator接口,实现类的对象,进而实现集合的遍历
equal(Object obj):判断两个集合中所有元素是否完全相同
将数组转成集合: Collection coll1 = Arrays.asList(1, 2, 3);
将集合转成数组:Object [ ] obj=coll1.toArray();
//创建集合对象 Collection<String> c = new ArrayList<String>(); //boolean add(E e):添加元素 // System.out.println(c.add("hello")); // System.out.println(c.add("world")); // System.out.println(c.add("world")); c.add("hello"); c.add("world"); c.add("java"); //boolean remove(Object o):从集合中移除指定的元素 // System.out.println(c.remove("world")); // System.out.println(c.remove("javaee")); //void clear():清空集合中的元素 // c.clear(); //boolean contains(Object o):判断集合中是否存在指定的元素 // System.out.println(c.contains("world")); // System.out.println(c.contains("javaee")); //boolean isEmpty():判断集合是否为空 // System.out.println(c.isEmpty()); //int size():集合的长度,也就是集合中元素的个数 System.out.println(c.size()); //输出集合对象 System.out.println(c);
3>. 数组转换成集合 [ 掌握 ]
①. 数组换成成集合,虽然不能增加或减少元素,但是可以用集合的思想操作数组,也就是说可以使用其他集合中的方法 [ 除了增加和减少都能使用 ]
②. 基本数据类型的数组转换成集合,会将整个数组当成一个对象转换
③. 将数组转成集合,数组必须是引用数据类型
String[]arr={"a","b","c"};
List<String> list= Arrays.asList(arr);
System.out.println(list);
//list.add("d");//UnsupportedOperationException
//System.out.println(list);
int[]arr2={11,22,33,44,55};
//List<int[]ar> listInt=Arrays.asList(arr2);
List listInt=Arrays.asList(arr2);
System.out.println(listInt);//输出的是地址值[[I@3578436e]
1
2
3
4
5
6
7
8
9
//基本数据类型的数组转换成集合,会将整个数组当成一个对象转换
Integer[]arr={11,22,33,44,55};
List<Integer>list=Arrays.asList(arr);
System.out.println(list);//[11, 22, 33, 44, 55]
//将数组转成集合,数组必须是引用数据类型
1
2
3
4
5
4>. 集合转数组 [ 加泛型的 ]
当集合转换数组时,数组的长度如果<=集合的size,转换后的数组长度等于集合的size
当集合转换数组时,数组的长度如果>=集合的size,分配的数组长度,就和你指定的长度一样[new String[10]]
//谁大集合长度就等于谁 ArrayList<String>list=new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); String[]str=list.toArray(new String[0]); for(String str2:str){ System.out.println(str2); }
5>.集合的遍历方式 [ 迭代器iterator 和 增强for循环 ].
5.1. Iterator接口的概述
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合
Iterator<E> iterator ():返回此集合中元素的迭代器,通过集合的iterator () 方法得到
//迭代器的原理及源码解析 Iterator<String> iterator=coll.iterator();//是通过多肽的方式得到的Iterator的对象 /* public Iterator<E> iterator() { return new ArrayList.Itr(); } private class Itr implements Iterator<E> { ....... } */
5.2. 方法
boolean hasNext():如果迭代具有更多的元素,则返回true
E next():返回迭代中的下一个元素,并把指针向下移动一位
while(iterator.hasNext()){ /* Iterator中的常用方法 : E next():返回迭代中的下一个元素 boolean hasNext():如果迭代具有更多的元素,则返回true * */ //System.out.println(iterator.next()); String s=iterator.next(); System.out.println(s);//String类实现了toString方法 }
5.3. 增强for循环
增强for:简化数组和Collection集合遍历
它是JDK5 之后出现的,其内部原理就是一个Iterator迭代器
//使用增强for循环实现数组的便利遍历 @Test public void test3() { String[]str=new String[]{"aa","bb","cc"}; for(String s:str){ System.out.println(s); } } // 使用增强for循环实现集合的便利遍历 @Test public void test2() { Collection coll = new ArrayList(); coll.add("aa"); coll.add(11); for(Object i:coll){ System.out.println(i); } } }
5.4>. 普通for循环
for (int i = 0; i <list.size() ; i++) { Student s=list.get(i); System.out.println(s.getId()+" "+s.getName()); }
6>. 三种迭代能否删除
①. 普通for循环,可以删除,但是索引要 i- -
②. 迭代器,可以删除,但是必须使用迭代器自身的remove方法,否则会出现并发修改异常
③. 增强for循环不能删除
public void fun1(){ ArrayList<String> list=new ArrayList<>(); list.add("AAA"); list.add("BBB"); list.add("BBB"); list.add("CCC"); list.add("DDD"); //1.普通for循环进行删除 /* for (int i = 0; i < list.size(); i++) { String str=list.get(i); if("BBB".equals(str)){ //两个BBB在一起的话,会留住一个BBB //两个BBB分割的话,两个都会删除 //要想删除两个BBB必须使用i--,每次删除后,指针又回到原来的位置 //list.remove(i); list.remove(i--); } }*/ //2.迭代器删除 /* Iterator<String>it=list.iterator(); while(it.hasNext()){ if("BBB".equals(it.next())){ //ConcurrentModificationException //list.remove("BBB"):不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常 it.remove(); } }*/ /* for(Iterator<String>it2=list.iterator();it2.hasNext();){ if("BBB".equals(it2.next())){ //ConcurrentModificationException //list.remove("BBB"):不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常 it2.remove(); } }*/ //3.增强for循环 /* 增强for循环不能删除,因为底层是一个iterator * */ for(String str2:list){ if("BBB".equals(str2)){ // list.remove("BBB"); } } System.out.println(list); }
7>.Collection集合储存学生对象并遍历
2. List接口
1>. List集合的概述
①. 元素都带有索引
②. List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引
有序:储存 和 取出 的元素顺序一致
可重复:储存元素可以重复
2>. list集合特有方法
①. 增: void add( int index,E element):添加一个元素
②. 删 E remove(int index) 删除指定索引位置的元素,返回被删除的元素
③. 改E set(int index,Object ele):设置指定索引位置的元素,返回被修改的元素
④. 查 E get(int index):获取指定索引的元素
//遍历集合 for (int i = 0; i <list.size() ; i++) { Student s=list.get(i); System.out.println(s.getId()+" "+s.getName()); }
public class ListDemo02 { public static void main(String[] args) { //创建集合对象 List<String> list = new ArrayList<String>(); //添加元素 list.add("hello"); list.add("world"); list.add("java"); //void add(int index,E element):在此集合中的指定位置插入指定的元素 // list.add(1,"javaee"); //IndexOutOfBoundsException // list.add(11,"javaee"); //E remove(int index):删除指定索引处的元素,返回被删除的元素 // System.out.println(list.remove(1)); //IndexOutOfBoundsException // System.out.println(list.remove(11)); //E set(int index,E element):修改指定索引处的元素,返回被修改的元素 // System.out.println(list.set(1,"javaee")); //IndexOutOfBoundsException // System.out.println(list.set(11,"javaee")); //E get(int index):返回指定索引处的元素 // System.out.println(list.get(1)); //IndexOutOfBoundsException // System.out.println(list.get(11)); //输出集合对象 // System.out.println(list); //遍历集合 // System.out.println(list.get(0)); // System.out.println(list.get(1)); // System.out.println(list.get(2)); //用for循环改进遍历 for (int i=0; i<list.size(); i++) { String s = list.get(i); System.out.println(s); } } }
3>. List并发修改异常
// 需求 我有一个集合: List<String> list=new ArrayList<>(); 里面有三个元素:list.add("hello");list.add("world");list.add("java"); 遍历集合,得到每一个元素,看有没有 " world "这个元素,如果有,我就添加一个" javaEE" 元素
异常的原因:迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致 [ 重点 ]
解决方案:使用for循环进行遍历,list中的get方法不会对 expectedModCount = modCount进行判断,可以添加成功 @Test public void fun5(){ List<String> list=new ArrayList<>(); list.add("hello"); list.add("world"); list.add("java"); Iterator<String> iterator=list.iterator(); for (int i = 0; i < list.size(); i++) { String s=list.get(i); if(s.equals("world")){ list.add("javaEE"); } } /* while(iterator.hasNext()){ String s=iterator.next(); if(s.equals("world")){ list.add("javaEE"); //遍历的同时在修改元素,并发修改ConCurrentModificationException } }*/ System.out.println(list); }
4>. 列表迭代器 ListIterator
4.1. ListIterator 的概述
通过list集合的 listIterator()方法得到,所以说它是list集合中特有的迭代器
用于允许程序员沿任一方向遍历列表的迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
4.2. ListIterator中的常用方法
E next():返回迭代中的下一个元素
boolean hasNext():如果迭代具有更多的元素,则返回true
E previous():返回列表中的上一个元素
bloolean hasPrevious(): 如果此列表迭代器在相反方向遍历列表时具有更多的元素,则返回true
void add(E e):将制定的元素插入列表[ 重点掌握 ]
①. 列表迭代器的add方法 会让 expectedModCount = modCount,不会出现并发修改异常
②. 而集合中的add方法,会直接把修改的次数加上1,这样expectedModCount != modCount,下次当再次调用next()的方法的时候,会调用checkForComodification()方法,当ExpectedModCo unt != modCount的时候会抛出修改并发异常
// listIterator接口中的add()方法会 expectedModCount = modCount; public void add(E e) { checkForComodification(); try { int i = cursor; ArrayList.this.add(i, e); cursor = i + 1; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
逆向遍历,必须要有正向遍历在前面才能遍历出来;要是没有正向遍历,直接逆向遍历,逆向遍历的指针从0索引开始,这个时候向后遍历便没有要遍历的元素 @Test public void fun6(){ List<String>list=new ArrayList<>(); list.add("hello"); list.add("world"); list.add("javeEE"); //通过list集合的listItrator()方法得到 ListIterator<String> lit=list.listIterator(); System.out.println("正向遍历:"); while(lit.hasNext()){ String s=lit.next(); System.out.print(s+"\t");//hello world javeEE } System.out.println(); System.out.println("逆向遍历:");//一般很少运用 while(lit.hasPrevious()){ String s=lit.previous(); //要是没有正向遍历直接逆向遍历,指针在0的位置,向上找没有元素,所以出打印空 System.out.print(s+"\t");//javeEE world hello } //获取列表迭代器 while(lit.hasNext()){ String s=lit.next(); if(s.equals("world")){ //列表迭代器的add方法 会让 expectedModCount = modCount; lit.add("xiaozhi"); } } System.out.println(list);//[hello, world, xiaozhi, javeEE] }
5>. ArrayList
5.1. 什么是ArrayList
ArrayList<E>: 在所有E的地方我们使用引用数据类型替换即可 举例:ArrayList<String>、ArrayList<Student>
可调节大小的数组实现
< E >: 是一种特殊的数据类型,范型
5.2. ArrayList构造 和 增删改查
public ArrayList():创建一个空的集合对象
public boolean add(E e):将指定的元素追加到此集合的末尾
public void add(int index , E element):在此集合中指定位置插入指定的元素
public boolean remove(Objec o):删除指定的元素,返回删除是否成功 [ 重写了equals方法 ]
public E remove(int index):删除指定索引处的元素,返回被删除的元素 [ <> 返回的是什么,E就是什么类型]
public E set(int index , E element) :修改指定索引处的元素,返回被修改的元素
public E get(int index):返回指定索引处的元素
public int size() :返回集合中的元素的个数
//ArrayList<String> array=new ArrayList<>(); ArrayList<String> array=new ArrayList<String>(); //public boolean add(E e) array.add("AAA"); array.add("BBB"); array.add("CCC"); //public void add(int index,E element) array.add(0,"BeforeAAA"); System.out.println(array);
5.3. 遍历集合 [ 掌握 ]
@Test public void fun8(){ List<String>list=new ArrayList<>(); list.add("hello"); list.add("world"); list.add("xiaozhi"); //遍历集合的通用格式 //E get(int i) for(int i=0;i<list.size();i++){ String s=list.get(i); System.out.print(s+"\t"); }
5.4. ArrayList 中去除重复字符串元素方式
1. 基本数据类型的去重
分析 :
1.创建新集合
2.根据传入的集合[ 老集合 ] 获取迭代器
3.遍历老集合
4.通过新集合判断是否包含老集合中的元素,如果包含就不添加,如果不包含就添加
public class DemoInteger { public static void main(String[] args) { //ArrayList去除集合中字符串的重复值(字符串的内容相同) //思路: 创建新集合方式 /* 创建新集合将重复的元素去掉 1.明确返回值类型,返回ArrayList 2.明确参数类表ArrayList * */ ArrayList<String> list=new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("bb"); list.add("cc"); ArrayList<String>arrayList=getSingle(list); Iterator<String> iterator = arrayList.iterator(); while (iterator.hasNext()){ System.out.print(iterator.next()+"\t"); } } public static ArrayList getSingle(ArrayList list){ ArrayList<String> listNew=new ArrayList<>(); Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ String str1=iterator.next(); if(!listNew.contains(str1)){ listNew.add(str1); } } return listNew; }
2. 引用类型的去重
①. contains( ): 方法判断是否包含,依赖的是equals方法。没有重写equals之前比较的是地址值,重写后比较的时候属性值
②. remove( ) :方法的底层也是依赖equals方法。没有重写equals之前比较的是地址值,重写后比较的时候属性值
//Person [ 重点是equals方法的重写 ] public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person(){ } //重点是要重写equals方法 @Override public boolean equals(Object o) { Person p=(Person)o; return this.name.equals(p.name)&&this.age==p.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 String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
public class ArrayListDemo { public static void main(String[] args) { // 创建集合 ArrayList list=new ArrayList(); //添加元素 list.add(new Person("zhangsan",25)); list.add(new Person("zhangsan",25)); list.add(new Person("lisi",23)); list.add(new Person("lisi",23)); //去除重复元素 ArrayList listNew=getSingle(list); System.out.println(listNew); System.out.println("--------"); listNew.remove(new Person("zhangsan",25)); System.out.println(listNew); } //contains 方法判断是否包含,依赖的是equals方法 //remove :方法的底层也是依赖equals方法。没有重写equals之前比较的是地址值,重写后比较的时候属性值 public static ArrayList getSingle(ArrayList list){ ArrayList listNew=new ArrayList(); Iterator iterator = list.iterator(); while(iterator.hasNext()){ Object obj=iterator.next(); if(!listNew.contains(obj)){ listNew.add(obj); } } return listNew; } }
6>. Vector [ 了解 ]
Vector v=new Vector(); v.addElement("AAA"); v.addElement("BBB"); v.addElement("CCC"); Enumeration en=v.elements();//获取枚举 while (en.hasMoreElements()){//判断集合中是否有元素 System.out.println(en.nextElement());//获取集合的元素 }
3. 数据结构 [ 栈、队列 ]
1>.栈 [先进后出的模型 ]
压/进栈:数据进入栈模式的过程
弹/出栈:数据离开栈模型的过程
2.> 队列 [ 先进先出的模型 ]
入队列:数据从后段进入队列模型的过程
出队列:数据从前端离开队列模型的过程
3>.数组
数组是一种查询快,增删慢的模型
4>. 链表
链表的每个元素称为节点,包含:数据和下一个节点的地址
4. 数据结构之数组和链表 [ 掌握 ]
数组:查询快修改也快 [ 能很快找到索引值 ]; 增、删 很慢 [ 不管是增还是删,都会创建一个新数组,拷贝等 ] ;每增加一倍,元素的就扩容一倍
链表:查询修改是慢的 [ 查询只能从两头开始,全部找];增、删很快
5. list的三个子类的特点
1>.ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
2>.Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低
3>.LinkedList
底层数据结构是链表,查询慢,增删快
查修慢的原因是:链表的底层会从两端往中间查询;增删快的原因是:只在一个链表中发生,增加和删除会断开两个地址值,然后指向要添加的元素
线程不安全,效率高
4>. 面试题目[ 重点掌握 ]
1.Vector 和 ArrayList 的区别
Vector 是线程安全的,效率低
ArrayList 是现成不安全的,效率高
共同点:都是数组实现的,增删慢,改查快
2.ArrayList 和 LinkedList的区别
ArrayList 底层是数组结果,查询和修改快
linkedList 底层是链表结构的,增和删比较快,查询和修改比较慢
共同点:都是线程不安全的
5>.List有三个儿子,我们到底使用谁呢?
查询多用 ArrayList
增删多用 LinkedList
如果都多 ArrayList
6. 泛型 [ generic ]
1>.generic概述和基本使用
< > 中放的必须是引用数据类型
泛型使用的注意事项:前后的泛型必须一致, 或者后面的泛型可以省略不写 [ 1.7的新特性 ]
泛型最好不要定义成Object,没有意义
ArrayList<Object>list=new ArrayList<Person>();//报错 ArrayList<Person>list=new ArrayList<Person>();//1.7版本的新特性 ArrayList<Person>list=new ArrayList<>();//1.7版本的新特性
2>. 泛型类 的概述和使用
定义格式:public class 类名 < 泛型类型1....>{ }
注意事项:泛型类型必须是引用类型
public class Tools<Q> { private Q q; public void setQ(Q q){ this.q=q; } public Q getQ(){ return q; } }
3>. 泛型方法的概述和使用
格式:修饰符<类型> 返回值类型 方法名(类型 变量名){} public class Tools<Q> { private Q q; public void setQ(Q q){ this.q=q; } public Q getQ(){ return q; } //方法泛型最好要与类的泛型一致 public void show(Q q){ System.out.println(q); } //如果不一致,需要在方法上声明该泛型 public <T>void show2(T t){ System.out.println(t); } //Q 是创建对象的时候才给赋值,静态方法必须声明自己的泛型 /* public static void print(Q q){ //报错 }*/ public static<Q> void print(Q q){ //这里的Q与类上面的泛型是不同的泛型 } }
@Test public void fun1(){ Tools<String>tools=new Tools<>(); tools.show("abc");//abc tools.show2("123");//123 }
4>.泛型接口的概述和使用
interface Inter<T>{ public void show(T t); } //第一种方式:推荐使用 class Demo implements Inter<String>{ public void show(String t) { System.out.println(t); } } //第二种方式 /* class Demo<T> implements Inter<T>{ //没有必要在实现接口的时候给自己类添加泛型 @Override public void show(T t) { System.out.println(t); } }*/
5>. 泛型高级之通配符
①. 泛型通配符<?>:任意类型,如果没有明确,那么就是Object以及任意的Java类
②. ?extends E :向下限定,E及其子类 [ 泛型固定上边界 ]
③. ? super E: 向上限定, E及其父类 [ 泛型固定下边界 ]
//当右边的泛型是不确定时,左边可以指定? List<?> list=new ArrayList<Integer>(); //?extends E :向下限定,E及其子类 (?是子类,E是父类) // addAll(Collection < ? extends E >) 括号里面可以放E及其子类 // 因为我们不确定有多少子类,所以我们使用?代替 public class Student extends Person { private String name; private int age; public Student() { } public Student(String name, int age) { super(name,age); } }
ArrayList<Person>list2=new ArrayList<>(); list2.add(new Person("张三",23)); list2.add(new Person("李四",24)); list2.add(new Person("王五",25)); ArrayList<Student>list3=new ArrayList<>(); list2.add(new Person("赵六",26)); list2.add(new Person("周七",27)); list2.addAll(list3); System.out.println(list2);//把子类放入到父类中是可以的 //list3.addAll(list2);//在父类放入到子类中是不可以的 // public boolean addAll(Collection<? extends E> c) {
//TreeSet(Comparator<? super E>comparator) TreeMap(Comparator<? super E>comparator)
//类型通配符上限:<? extends 类型> //List<? extends Number> list1=new ArrayList<Object>(); 报错 List<? extends Number> list2=new ArrayList<Number>(); List<? extends Number> list3=new ArrayList<Integer>(); //类型通配符下限<? super 类型> //List<? super Number> list4=new ArrayList<Integer>();报错 List<? super Number> list5=new ArrayList<Number>(); List<? super Number> list4=new ArrayList<Object>();
7. JDK 5的新特性
1>. 增强for循环[ 底层是iterator ]
2>. 可变参数
可变参数概述:定义方法的时候不知道定义多少个参数
格式:修饰符 返回值类型 方法名(数据类型 ... 变量名){ }
注意事项:①. 这里的变量其实是一个数组 ②. 如果一个方法有可变参数,那么,可变参数肯定是最后一个
3>. JDK5 的新特性自动装箱和拆箱
自动装箱:把基本数据类型转换成包装类型
自动拆箱:把包装类型转换为基本数据类型
注意事项: 在使用时,Integer x=null; 代码会出现NullPointerException,建议先判断是否为null,然后使用
Integer i3=null; int a=i3+100; System.out.println(a);//NullPointerException //底层用i3调用的是intValue() ,但是i3是null,null调用方法就会出现空指针异常
8.Set
1>.HashSet [ Set的主要实现类 ]
1.1. HashSet 的概述
①. 无序性!=随机性 [ 正在的无序性是指元素在底层存储的位置是无序的 ]
②. 不可重复性:当向set中添加进相同的元素的时候,后面的这个不能添加进去
③. 没有带索引的方法,所以不能用普通for循环遍历
④. 底层数据结构是哈希表
HashSet<String>hs=new HashSet<>(); boolean b1=hs.add("a"); boolean b2=hs.add("a"); hs.add(null); System.out.println(hs); //HashSet的继承体系中,有重写toString()方法 System.out.println(b1); System.out.println(b2); //只要能用迭代器迭代的,就可以使用增强for循环遍历 for(String str:hs){ System.out.println(str);//[a] }
1.2. 哈希值
1.哈希值的介绍
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值:public int hashCode():返回对象的哈希码值
特点:①.同一个对象多次调用hashCode() 方法返回的哈希值是相同的
②.默认情况下,不同对象的哈希值是不相同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
1.3. 哈希值原理 [掌握]
1.4. 自定义对象的唯一性
要求添加进Set中的元素所在的类,一定要重写equal()和 hashCode()方法,进而保存不可重复性 [ 掌握 ]
hashCode():属性相同的对象返回值必须相同,属性不同的返回值尽量不同(提高效率)
equals():属性相同返回true,属性不同返回false,返回false的时候就存储
[ 掌握关于equals方法和hashCode方法底层实现原理 ] /* public static int hashCode(Object a[]) { if (a == null) return 0; int result = 1; for (Object element : a) result = 31 * result + (element == null ? 0 : element.hashCode()); return result; } 为什么是31? 1.31是质数,质数是能被1和它本身整除的数 2.31这个数既不大也不小 3.31这个数好算,2的5次方-1,向左移动5位数-1 * */ @Override public boolean equals(Object o) { if (this == o) return true;//调用的对象和传入的对象是同一个对象,直接返回true if (o == null || getClass() != o.getClass()) return false;//传入的对象为空或者字节码文件不相同,返回false Person person = (Person) o;//向下转型 return age == person.age && Objects.equals(name, person.name); //如果调用对象的年龄和传入对象的年龄 相同并且 调用对象的姓名和传入对象的姓名相同返回true }
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } 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 int hashCode() { return Objects.hash(name, age); } @Override public boolean equals(Object o) { if (this == o) return true;//调用的对象和传入的对象是同一个对象,直接返回true if (o == null || getClass() != o.getClass()) return false;//传入的对象为空或者字节码文件不相同,返回false Person person = (Person) o;//向下转型 return age == person.age && Objects.equals(name, person.name); //如果调用对象的年龄和传入对象的年龄 相同并且 调用对象的姓名和传入对象的姓名相同返回true } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
HashSet<Person>hs=new HashSet<>(); hs.add(new Person("zhangsan",23)); hs.add(new Person("zhangsan",23)); hs.add(new Person("zhangsan",23)); hs.add(new Person("lisi",24)); hs.add(new Person("lisi",24)); hs.add(new Person("lisi",24)); System.out.println(hs.size());//2 System.out.println(hs);//[Person{name='lisi', age=24}, Person{name='zhangsan', age=23}] }
1.5. HashSet的原理 [ 掌握 ]
当HashSet调用add()方法储存对象的时候,先调用对象的hashCode方法得到一个哈希值,然后在集合中查找是否有哈希值相同的对象。如果没有哈希值相同的对象就直接存入集合;如果有哈希值相同的对象,就和哈希值相同对象逐个进行equals()比较,比较结果为false就存入,true则不存
2>.LinkedHashSet
①. 哈希表和链表实现的Set接口,具有可预测的迭代次序
②. 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
③. 由哈希表保证元素唯一,也就是说没有重复的元素
@Test public void testLinkHashSet(){ Set set=new LinkedHashSet(); set.add(123); set.add(456); set.add("AAA"); set.add("BBB"); set.add(null); Iterator iterator=set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next());//123 456 AAA BBB null }
3>. Set的练习
①. 编写一个程序,获取10个1-20的随机数,要求随机数不能重复,并把最终的结果打印在控制台上
//编写一个程序,获取10个1-20的随机数,要求随机数不能重复,并把最终的结果打印在控制台上 //1.有Random类创建随机数对象 Random random=new Random(); //2.需要储存10个随机数,而且不能重复,所以我们用hashSet集合 HashSet<Integer>hs=new HashSet<>(); //3.如果HashSet的size是<10 就可以不断的储存,如果大于等于10就停止储存 while(hs.size()<=10){ //4.4.通过Randeom类中的nextInt(n)方法获取1-20之间的随机数,并将这些数字储存在hashSet中 int i=random.nextInt(20)+1; hs.add(i); } for(Integer hs2:hs){ System.out.println(hs2); }
②.使用Scanner从键盘读取一行输入,去除重复字符,打印不同的那些字符
Scanner sc=new Scanner(System.in); System.out.println("请键盘输入字符串"); String str=sc.nextLine(); HashSet<Character> hs=new HashSet<>(); char[] chs = str.toCharArray(); for (int i = 0; i < chs.length; i++) { hs.add(chs[i]); } Iterator<Character> iterator = hs.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } /*for(Character hs2:hs){ System.out.print(hs2); }*/
③.去除list中重复的元素
//将集合中重复的元素去掉 @Test public void fun7(){ ArrayList<String>list=new ArrayList<>(); list.add("AAA"); list.add("AAA"); list.add("AAA"); list.add("AAA"); list.add("AAA"); list.add("AAA"); list.add("BBB"); list.add("BBB"); list.add("CCC"); list.add("CCC"); getSingle(list); // 打印 System.out.println(list); } public static void getSingle(ArrayList<String> list) { /*LinkedHashSet<String>lhs=new LinkedHashSet<>(); Iterator<String> iterator = list.iterator(); while(iterator.hasNext()){ lhs.add(iterator.next()); } for(String lhs2:lhs){ System.out.print(lhs2+"\t"); }*/ //1.创建一个LinkedHashSet集合 LinkedHashSet<String>lhs=new LinkedHashSet<>(); //2.将List集合中所有的元素添加到LinkedHashSet集合 lhs.addAll(list); //3.将List 集合中元素清除 list.clear(); //4.将LinkedHashSet集合中的元素添加到List集合中 list.addAll(lhs); }
4>. TreeSet
4.1.TreeSet的特点
①.元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator) :根据指定的比较器进行排序
②. 没有带索引的方法,所以不能使用普通for循环遍历
③. 由于是Set集合,所以不包含重复元素的集合
4.2. 自然排序的原理和图解
1. 自然顺序(Comparable)
①. TreeSet 类的add()方法中会把存入的对象提升为Comparable类型
②. 调用对象的CompableTo()方法和集合中的对象比较
③. 根据CompableTo()方法返回的结果进行储存
TreeSet底层是一个二叉树,两个叉。小的存储在左边[ 负数 ],大的存储在右边[ 正数 ],相等就不存 [ 0 ]。CompareTo方法,在TreeSet集合如何存储元素取决于compareTo方法的返回值[ 掌握 ]
①. 返回的0,集合中只有一个元素
②. 返回-1,集合会将存储的元素倒序
③. 返回1,集合会怎么存就怎么取
/ /按照年龄排序 public int compareTo(Object o) { Person p=(Person)o; int num=this.age-p.age;//年龄是比较的主要条件 return num==0?this.name.compareTo(p.name):num;//姓名是比较的次要条件 } TreeSet<Person>ts=new TreeSet<>(); ts.add(new Person("张三",23)); ts.add(new Person("李四",13)); ts.add(new Person("王五",43)); ts.add(new Person("赵六",33)); System.out.println(ts); //[Person{name='李四', age=13}, Person{name='张三', age=23}, Person{name='赵六', age=33}, Person{name='王五', age=43}]
4.3. TreeSet存储自定义对象并遍历练习 [ 自然排序 ]
public class Person implements Comparable{ private String name; private int age; }
①. 按照姓名排序
//按照姓名排序 public int compareTo(Object o) { Person p=(Person)o; int num=this.name.compareTo(p.name); //姓名是主要条件,年龄是次要条件 return num==0?this.age-p.age:num; } TreeSet<Person>ts=new TreeSet<>(); ts.add(new Person("张三",23)); ts.add(new Person("张三",25)); ts.add(new Person("王五",43)); System.out.println('张'+0);//24352 System.out.println('李'+0);//26446 System.out.println('王'+0);//29579 System.out.println("ts = " + ts); //ts = [Person{name='张三', age=23}, Person{name='张三', age=25}, Person{name='王五', age=43}]
②. 按照姓名长度进行排序
public int compareTo(Object o) { Person p=(Person)o; int length=this.name.length()-p.name.length();//比较长度为主要条件 int num=length==0?this.name.compareTo(p.name):length;//比较内容为次要条件 return num==0?this.age-p.age:num;//比较年龄也为次要条件 } TreeSet<Person>ts=new TreeSet<>(); ts.add(new Person("zhangsan",23)); ts.add(new Person("lisi",25)); ts.add(new Person("wangwu",43)); System.out.println("ts = " + ts); //ts = [Person{name='lisi', age=25}, Person{name='wangwu', age=43}, Person{name='zhangsan', age=23}]
4.4. 比较器顺序(Comparator)
①. 创建TreeSet的时候可以制定一个Comparator接口
②. 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器中的顺序排序
③. 调用的对象是Comparator接口中compare()方法顺序
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
@Test public void fun4(){ /* 需求:将字符串按照长度排序 * */ //Comparator c=new CompareByLen(); //new TreeSet(Comparator ); TreeSet<String>ts=new TreeSet<>(new CompareByLen()); ts.add("aaaaaaaa"); ts.add("z"); ts.add("wc"); ts.add("nba"); ts.add("cba"); System.out.println(ts);//[n, bb, aaa] } class CompareByLen /* extends Object*/implements Comparator<String>{// @Override public int compare(String s1, String s2) { //按照字符串长度进行比较 int num=s1.length()-s2.length();//长度为主要条件 return num==0?s1.compareTo(s2):num;//内容为次要条件 } }
4.5. TreeSet原理 [ 掌握 ]
1. 自然顺序(Comparable)
①. TreeSet 类的add()方法中会把存入的对象提升为Comparable类型
②. 调用对象的CompableTo()方法和集合中的对象比较
③. 根据CompableTo()方法返回的结果进行储存
2. 比较器顺序(Comparator)
①. 创建TreeSet的时候可以制定一个Comparator接口
②. 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器中的顺序排序
③. 调用的对象是Comparator接口中compare()方法顺序
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
3. 两种方式的区别
TreeSet 如果传入Comparator,就优先按照Comparator .
TreeSet构造函数什么都不传,默认按照类中Comparable的顺序(没有就报错ClassCastE xception)
//java.lang.ClassCastException: com.itheima.TreeSetDemo4.Person cannot be cast to java.base/java.lang.Comparable 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 String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
@Test public void fun1(){ //1.按照年龄来排序 TreeSet<Person>ts=new TreeSet<>(); ts.add(new Person("张三",23)); ts.add(new Person("张三",23)); ts.add(new Person("张三",23)); ts.add(new Person("李四",24)); ts.add(new Person("王五",24)); System.out.println("ts = " + ts); }
4.6. 练习 [ 掌握 ]
①. 在一个集合中储存了无序并且重复的字符串,定义一个方法,让其有序(字典顺序)而且重复的输出
/* 分析: 1.定义一个List集合,并储存重复的无序的字符串 2.定义方法对其排序保留重复 3.打印list集合 * */ //1.定义一个List集合,并储存重复的无序的字符串 ArrayList<String>list=new ArrayList<>(); list.add("aaa"); list.add("aaa"); list.add("ccc"); list.add("bbb"); list.add("ccc"); list.add("ddd"); list.add("ddd"); list.add("aaa"); list.add("aaa"); //2.定义方法对其排序保留重复 sort(list); //3.打印list System.out.println(list); } /** 定义方法,排序并保留重复 分析: 1.创建TreeSet集合对象,因为String本身就具备比较功能,但是重复的不会保留,所以我们用比较器 2.将list集合中所有的元素添加到TreeSet集合中,对其排序保留重复 3.清空list集合 4.将TreeSet集合中排好序的元素添加到list集合中 * */ public static void sort(List<String> list){ // 1.创建TreeSet集合对象,因为String本身就具备比较功能,但是重复的不会保留,所以我们用 TreeSet<String>ts=new TreeSet<>(new Comparator<String>() { public int compare(String s1, String s2) { int num=s1.compareTo(s2);//比较内容为主要条件 return num==0?1:num;//返回1就不让你返回1 //保留重复 } }); //2.将list集合中所有的元素添加到TreeSet集合中,对其排序保留重复 ts.addAll(list); //3.清空list集合 list.clear(); //4.将TreeSet集合中排好序的元素添加到list集合中 list.addAll(ts); }
②. 从键盘接收一个字符串,程序对其中所有字符进行排序 [ 例如键盘输入: helloitcase程序打印: aceehillost ] [ 重点掌握 ]
//注意这里的自动装箱和拆箱的过程 // 1.键盘录入字符串,Scanner Scanner sc=new Scanner(System.in); System.out.println("请键盘输入一个字符串"); String str=sc.nextLine(); //2.将字符串转换为字符数组,可以拿到每一个字符 char[] chars = str.toCharArray(); //3.定义TreeSet集合,传入比较器对字符排序并保留重复 TreeSet<Character>ts=new TreeSet<>(new Comparator<Character>() { @Override public int compare(Character s1, Character s2) { // int num=s1-s2; //自动拆箱 int num=s1.compareTo(s2); return num==0?1:num; } }); //4.遍历字符数组,将每一个字符储存到TreeSet集合中 for (int i = 0; i < chars.length; i++) { ts.add(chars[i]); //自动装箱: 会使得char基本数据类型转成包装类型 } //5.遍历TreeSet集合,打印每一个字符 for(Character c:ts){ System.out.print(c); }
③. 程序启动后,可以从键盘接收多个整数,直到输入quit时结束输入,把所有输入的整数倒序排列打印
//1.创建Scanner对象,键盘录入 Scanner sc=new Scanner(System.in); //2.创建TreeSet集合对象,TreeSet集合中传入比较器 TreeSet<Integer>ts=new TreeSet<>(new Comparator<Integer>() { @Override public int compare(Integer i1, Integer i2) { int num=i2-i1;//这叫自动拆箱,使用i2-i1就是倒序了 //int num=i2.compareTo(i1) return num==0?1:num; } }); //3.无限循环不断接收整数,遇到quit退出,因为退出是quit,所以键盘录入的时候应该都以字符串的形式录入 while(true){ String str=sc.nextLine();//将键盘录入的字符储存在str中 if("quit".equals(str)){ break; } //4.判断是quit就退出,不是就将其转换成Integer,并添加到集合中 Integer i=Integer.parseInt(str);//本来这里变成int的,发生了自动装箱 int---Integer ts.add(i); } //5.遍历TreeSet集合并打印每一个元素 for(Integer integer:ts){ System.out.print(integer); }
9. Map接口
1>.Map接口的概述
Map [ 双列集合 ]与 Collection [ 单列集合 ] 并列存在
Map <K, V> 这里的K 、V 都应该是引用数据类型
HashSet底层依赖HashMap,单列底层依赖双列集合[ 理解 ]
2>. Map 集合的功能概述
2.1 添加功能.
V put(K key ,V value): 添加元素,返回的是以前的值
如果键是第一次储存,就直接储存元素,返回null;如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
Map<String,Integer> map=new HashMap<>(); Integer i1=map.put("张三",23); Integer i2=map.put("李四",23); Integer i3=map.put("王五",24); Integer i4=map.put("赵六",25); Integer i5=map.put("张三",26);//相同的键不储存,把原来的值覆盖 把被覆盖的值返回 System.out.println(map);//{李四=23, 张三=26, 王五=24, 赵六=25} //这里的i1--i4 都是null是因为开始的时候[张三,23][李四,23].... // 在集合中没有,这样把原来的值覆盖了,返回的是原来的值 System.out.println(i1);//null System.out.println(i2);//null System.out.println(i3);//null System.out.println(i4);//null System.out.println(i5);//23
删除功能
void clear():移除所有的键值对元素 V remove(Object key):根据键删除键值对元素,并把值返回 Map<String,Integer> map=new HashMap<>(); map.put("张三",23); map.put("李四",23); map.put("王五",24); map.put("赵六",25); //根据键删除元素,返回键对应的值 Integer value=map.remove("张三"); System.out.println(value);//23
2.3. 判断功能
boolean containsKey(Objecct key):判断是否包含指定的键 boolean containsValue(Objecct value):判断是否包含指定的值 //判断是否包含传入的键 System.out.println(map.containsKey("张三"));//true //判断是否包含传入的值 System.out.println(map.containsValue(23));//true
2.4. 长度功能 [ 掌握 ]
int size():返回集合中的键值对的个数 Map<String,Integer> map=new HashMap<>(); map.put("张三",26); map.put("李四",23); map.put("王五",24); map.put("赵六",25); Collection<Integer>coll=map.values(); System.out.println(coll);//[23, 23, 24, 25] System.out.println(map.size());//4
2.5. 获取功能 [ 掌握 ]
V get(Object key):根据键获取值
Set<K> keySet():获取集合中所有的键集合
Collection<V>values:获取集合中所有值集合
Set<Map.Entry<K,V>> entrySet():拿到所有的键值对对象
①. K getKey():得到entrySet中的键
②. V getValue():得到entrySet中的值
Map<String, Integer> map = new HashMap<>(); map.put("张三", 23); map.put("李四", 24); map.put("王五", 25); map.put("赵六", 23); //V get(Object key):根据键获取值 Integer i = map.get("张三");//26 Integer i2=map.get("小智");//没有的话返回null //1.获取所有的键 Set<String> keySet = map.keySet(); //iterator遍历 Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next();//获取每一个键 Integer value = map.get(key); //根据键获取值 System.out.println(key + "=" + value); } System.out.println("--------------"); //使用增强for循环 for (String key : map.keySet()) { Intger value=map.get(key); System.out.println(key + "=" +value ); }
interface Inter{ interface Inter2{ public void show(); } } //这里的Inter.Inter2 和Map.Entry一样的 class Demo implements Inter.Inter2{ @Override public void show() { } }
//2.根据键值对对象,获取键和值 //Map.Entry说明Entry是Map的内部接口,将键和值封装成了Entry对象,并储存在Set集合中 Set<Map.Entry<String,Integer>>entrySet=map.entrySet(); //获取每一个对象 Iterator<Map.Entry<String,Integer>>it=entrySet.iterator(); while(it.hasNext()){ //获取每一个Entry对象 //static class Entry<K,V> implements Map.Entry<K,V>{} Map.Entry<String,Integer>en=it.next();//父类引用指向子类对象 //Entry<String,Integer>en=it.next(); 子类对象 //根据键值对对象获取键 String key=en.getKey(); //根据键值对对象获取值 Integer value=en.getValue(); System.out.println(key+"="+value); } //增强for循环 for(Map.Entry<String,Integer> en:map.entrySet()){ System.out.println(en.getKey()+"="+en.getValue()); } for(Entry<String,Integer> en:map.entrySet()){ System.out.println(en.getKey()+"="+en.getValue()); }
3>.HashMap [ Map实现类之一 ]
①. Map 中的 key 用 Set 来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode( )和equals( )方法
②. HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCod e 值也相等
③. HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
注意:两个HashMap的hashcode相同,则他们的equal()方法不一定相同,若两个HashMap的equal()相同,则他们的hashcode一定相同
允许使用null键和null值,与HashSet一样,不保证映射的顺序
4>. 练习
①. HashMap集合键是Student值是String的案例[ 掌握 ]
// HashMap集合键是Student值是String的案例 public 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 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 && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
HashMap<Student,String>hm=new HashMap<>(); hm.put(new Student("张三",23),"北京"); String i=hm.put(new Student("张三",23),"上海"); hm.put(new Student("李四",24),"广州"); hm.put(new Student("王五",25),"深圳"); System.out.println(i); //没有重写equals方法之前,调用的是Object类的,会比较地址值 //没有重写equals方法和 hashCode方法的时候map中的元素有4个,重写后只有3个 //注意这里的张三对应的值是上海,因为把北京覆盖了 System.out.println(hm);//北京 //{Student{name='张三', age=23}=上海, Student{name='李四', age=24}=广州, Student{name='王五',
②. 统计字符串中每个字符出现的次数 [ 掌握 ]
思路:
1.键盘随机输入一个字符串
2.将字符串转换成字符数组
3.定义双列集合,存储字符串中字符以及字符出现的次数
4.遍历字符数组,获取每一个字符并将字符存储在双列集合中
5.存储过程做判断,如果集合中不包含这个键,就将该字符当做键,值为1存储
如果集合中包含这个键,就将值+1存储
6.打印双列集合获取字符出现的次数
//1.键盘随机输入一个字符串 Scanner sc=new Scanner(System.in); System.out.println("请输入一个字符串"); String line = sc.nextLine(); //2.将字符串转换成字符数组 char[] chars = line.toCharArray(); //3.定义双列集合,存储字符串中字符以及字符出现的次数 HashMap<Character,Integer>hm=new HashMap<>(); //4.遍历字符数组,获取每一个字符并将字符存储在双列集合中 for(char c:chars){ //5.如果不包含这个键,就将该字符当做键,值为1存储 /* if(!hm.containsKey(c)){ hm.put(c,1); }else{ hm.put(c,hm.get(c)+1); //这里发生了自动装箱 }*/ //这里先发生自动拆箱,后发生自动装箱 hm.put(c,!hm.containsKey(c)?1:hm.get(c)+1); } //6.打印双列集合获取字符出现的次数 Set<Map.Entry<Character,Integer>> en=hm.entrySet(); //①.迭代器答应 /* Iterator<Map.Entry<Character,Integer>> iterator=en.iterator(); while(iterator.hasNext()){ Map.Entry<Character,Integer> entry=iterator.next(); System.out.println("字母-->"+entry.getKey()+"在字符串中出现的次数是-->"+entry.getValue()); }*/ //②. 增强for /* for(Map.Entry<Character,Integer> en2:en){ System.out.println("字母-->"+en2.getKey()+"在字符串中出现的次数是-->"+en2.getValue()); } */ //③. Set<K> keySet(); 调用 V get(K) 调用键得到值 for(Character c2:hm.keySet()){ System.out.println("字母-->"+c2+"在字符串中出现的次数是-->"+hm.get(c2)); }
③. 单列集合嵌套双列集合
//需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并遍历 public class ArrayListDemo { public static void main(String[] args) { //创建ArrayList集合 ArrayList<HashMap<String,String>> list=new ArrayList<>(); //创建HashMap集合并添加键值对元素 HashMap<String,String>hs=new HashMap<>(); hs.put("唐智","岳阳"); hs.put("洋洋","广州"); hs.put("小幸","长沙"); HashMap<String,String>hs2=new HashMap<>(); hs2.put("诸葛亮","三国"); hs2.put("黄盖","三国"); hs2.put("卢布","三国"); HashMap<String,String>hs3=new HashMap<>(); hs3.put("孙悟空","西游"); hs3.put("猪八戒","西游"); hs3.put("沙僧","西游"); //将HashMap添加到ArrayList list.add(hs); list.add(hs2); list.add(hs3); //遍历ArrayList Iterator<HashMap<String, String>> iterator = list.iterator(); while(iterator.hasNext()){ //得到每一个HashMap中的对象 HashMap<String,String> hss=iterator.next(); //对得到的每一个HashMap对象进行遍历 for(String key:hss.keySet()){ String value=hss.get(key); System.out.println(key+"="+value); } } } }
④. 双列集合嵌套单列集合
public class HashMapDemo2 { public static void main(String[] args) { //创建HashMap集合 HashMap<ArrayList<String>,String>hm=new HashMap<>(); //创建ArrayList集合,添加元素 ArrayList<String>list=new ArrayList<>(); list.add("小智"); list.add("小幸"); list.add("洋洋"); ArrayList<String>list2=new ArrayList<>(); list2.add("诸葛亮"); list2.add("黄盖"); list2.add("卢布"); ArrayList<String>list3=new ArrayList<>(); list3.add("鹏翔"); list3.add("碰翔"); list3.add("朋翔"); //将ArrayList添加进HashMap集合中 hm.put(list,"岳阳"); hm.put(list2,"三国"); hm.put(list3,"湖南"); //遍历HashMap集合 for(ArrayList<String> key:hm.keySet()){ System.out.println(hm.get(key)); //遍历ArrayList 得到每一个HashMap中的 Iterator<String> iterator = key.iterator(); while(iterator.hasNext()){ String str=iterator.next(); System.out.println("\t"+str); } } } }
5>. LinkedHashMap
LinkedHashMap:使用链表维护添加进Map中的顺序, 故遍历Map时,是按添加的顺序遍历的
LinkedHashMap<String,Integer>lhm=new LinkedHashMap<>(); lhm.put("张三",23); lhm.put("李四",24); lhm.put("王五",25); lhm.put("赵六",26); System.out.println(lhm);//{张三=23, 李四=24, 王五=25, 赵六=26}
6>.TreeMap [ 和TreeSet一样 ]
自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,调用对象的comparaTo()方法和集合中的对象进行比较,根据CompableTo()方法返回的结果进行储存 [ 所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException ]
选择器排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
TreeMap集合键是Student值是String的案例
//自然排序 public class Student implements Comparable<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 String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Student s) { //以年龄为主要条件,姓名为次要条件 int num=this.age-s.age; return num==0?this.name.compareTo(s.name):num; } }
TreeMap<Student,String> tm=new TreeMap<>(); tm.put(new Student("张三",23),"北京"); tm.put(new Student("李四",13),"上海"); tm.put(new Student("王五",33),"广州"); tm.put(new Student("赵六",43),"北京"); System.out.println(tm); //{Student{name='李四', age=13}=上海, Student{name='张三', age=23}=北京, // Student{name='王五', age=33}=广州, Student{name='赵六', age=43}=北京}
/
//选择器排序 TreeMap<Student,String> tm=new TreeMap<>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { //按照姓名比较 int num=s1.getName().compareTo(s2.getName()); return num==0?s1.getAge()-s2.getAge():num; } }); tm.put(new Student("张三",23),"北京"); tm.put(new Student("李四",13),"上海"); tm.put(new Student("王五",33),"广州"); tm.put(new Student("赵六",43),"北京"); System.out.println(tm); //{Student{name='张三', age=23}=北京, Student{name='李四', age=13}=上海, //Student{name='王五', age=33}=广州, Student{name='赵六', age=43}=北京}
7>. Hashtable
面试题目 [ 掌握 ]
HashMap和Hashtable的区别
- 共同点:
底层都是哈希算法,都是双列集合
- 区别
1.HashMap是线程不安全的,效率高,jdk1.2版本
Hashtable是现场安全的,效率低,jdk1.0版本
2.HashMap可以存储null键和null值
Hashtable不可以存储null键和null值
8>. 操作集合的工具类:Collections [是一个类]
1. Collections中的概述
Collections 是一个操作 Set、List 和 Map 等集合的工具类
Collections中都是静态方法
①. public static <T> void sort(List<T>list):对集合进行升序
②. public static <T> reverse(List<?> list):将集合顺序反转
③. public static <T> shuffle(List<?> list):随机置换,可以用来洗牌
ArrayList<String> list=new ArrayList<>(); list.add("b"); list.add("a"); list.add("d"); list.add("c"); System.out.println(list);//[b, a, d, c] Collections.sort(list); System.out.println(list);//[a, b, c, d] //根据默认排序结果获取集合中最大值 System.out.println("max:"+Collections.max(list));//d System.out.println("开始的时候的排序是"+list);//[b, a, d, c] Collections.reverse(list); System.out.println("反转后时候的排序是"+list);//[c, d, a, b] System.out.println(list);//[b, a, d, c] Collections.shuffle(list);//反转集合 System.out.println(list); //[d, c, b, a] [a, d, b, c] [a, b, d, c]....
ArrayList<String>list=new ArrayList<>(); list.add("b"); list.add("c"); list.add("a"); list.add("d"); System.out.println(list); //如果索引包含在列表中,则返回搜索的索引,否则返回(-(插入点)-1) System.out.println(Collections.binarySearch(list,"c"));//1 System.out.println(Collections.binarySearch(list,"e"));//-5
2. Collection的练习
ArrayList存储学生对象,使用Collections对ArrayList进行排序
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
思路:
1:定义学生类
2:创建ArrayList集合对象
3:创建学生对象
4:把学生添加到集合
5:使用Collections对ArrayList集合排序
6:遍历集合
public class CollectionsDemo02 { public static void main(String[] args) { //创建ArrayList集合对象 ArrayList<Student> array = new ArrayList<Student>(); //创建学生对象 Student s1 = new Student("linqingxia", 30); Student s2 = new Student("zhangmanyu", 35); Student s3 = new Student("wangzuxian", 33); Student s4 = new Student("liuyan", 33); //把学生添加到集合 array.add(s1); array.add(s2); array.add(s3); array.add(s4); //使用Collections对ArrayList集合排序 //sort(List<T> list, Comparator<? super T> c) Collections.sort(array, new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { //按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序 int num = s1.getAge() - s2.getAge(); int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; return num2; } }); //遍历集合 for (Student s : array) { System.out.println(s.getName() + "," + s.getAge()); } } }
String、StringBuffer、StringBuilder
1. String类
1>.String类的概述
String 类在java.lang包下,所以使用的时候不需要导包
String类代表字符串,JAVA程序中所有的双引号字符串,都是String类的对象,包括 " " [ 就算没有new,也照样是 ]
如果在编程的过程中每次感觉字符串的内容发生了改变,那么一定是产生了一个新的字符串对象
2>. 字符串的特点
字符串不可变,它们在值在创建后不能被更改
虽然String的值是不可变的,但是它们可以被共享
字符串效果上相当于字符数组(char[ ]) ,但是底层原理是字节数组(byte[ ])
注意:在jdk1.8前是用char[ ] 数组存储的,JDK1.9中是用byte[ ] 数组存储的
3>. Stirng类的构造方法
public String():创建一个空白字符对象,不含有任何内容
public String (char[ ]chs):根据字符数组的内容,来创建字符串对象
public String(byte [ ]bys):根据字节数组的内容,来创建字符串对象
String s=" abc ":直接赋值的方式创建字符串对象,内容就是abc [ 推荐 ]
注意:直接写上双引号,就是字符串对象
String s1=new String(); System.out.println("s1"+s1); //public String(char[]chs):根据字符数组的内容,来创建字符串对象 char[] chs={'a','b','c'}; String s2=new String(chs); System.out.println("s2\t"+s2); //public String(byte[]bys):根据字节数组的内容,来创建字符串对象 byte[] bys={97,98,99}; String s3=new String(chs); System.out.println("s3\t"+s3); //String s="abc":直接赋值的方式创建字符串对象,内容就是abc String s4="abc"; System.out.println("s4\t"+s4);
4>. String对象的特点
①. 字符串常量池 [ 在方法区中 ]:程序当中直接写上的双引号字符串,就在字符串常量池中
②. 通过new创建的字符串对象,每一次new都会申请一个内存空间,虽然内容相同,但是地址值不同
重点掌握
String str1="abc"; String str2="abc"; char[]charArray={'a','b','c'}; String str3=new String(charArray); System.out.println(str1==str2);//true System.out.println(str1==str3);//fasle System.out.println(str2==str3);//fasle
5>. 字符串的比较
1. 使用 == 做比较
基本类型:比较的是数据值是否相同
引用类型:比较的是地址值是否相同
2. equals方法
public boolean equals (Object anObject): 参数可以是任何对象,只有参数是一个字符串并且内容相同才会给true;否则返回false
public boolean equalsIgnoreCase(String str):忽略大小写,进行内容比较
3. 注意事项
①.任何对象都能用Object进行接收
②.equals 方法具有对称性,也就是a.equals(b)和 b.equals(a)效果一样
③.如果比较双方是一个常量一个变量,推荐把常量字符串写在前面
推荐:“abc”.eqauls(str) 不推荐:str.equals(“hello”)
char[] chs={'a','b','c'};
String s1=new String(chs);
String s2=new String(chs);
String s3="abc";
String s4="abc";
//比较字符串对象地址值是否相同
System.out.println(s1==s2);//false
System.out.println(s1==s3);//false
System.out.println(s3==s4);//true
System.out.println("=======");
//比较字符串内容是否相同
System.out.println(s1.equals(s2));//true
System.out.println(s1.equals(s3));//true
System.out.println(s1.equals(s4));//true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
6>. String类的常见面试题
//1.判断定义为Stirng类型的str1和str1是否相等
//常量池中没有这个字符串对象,就创建一个,如果有,就直接引用
String str1="abc";
String str2="abc";
System.out.println(str1 == str2);//true
System.out.println(str1.equals(str2));//true
1
2
3
4
5
6
//2.下面这句话在内存中创建了几个对象
//创建了两个对象,一个在堆内存中,一个在常量池中
String str3=new String("abc");
1
2
3
//3.判断定义为String类型的str4和str5是否相等
//str4 中的地址是堆内存中的,而str5的地址是常量池中的
String str4=new String("abc");
String str5="abc";
System.out.println(str4==str5);/false
System.out.println(str4.equals(str5));//true
1
2
3
4
5
6
Java语言有常量优化机制
//4.判断定义为Stirng类型的str6和str7是否相等
//byte b=3+4 在编译的时候就变成了7,把7赋值给b,常量优化机制
String str6="a"+"b"+"c"; //这是三个常量字符相加
String str7="abc";
System.out.println(str6==str7);//true,java中有常量优化机制
System.out.println(str6.equals(str7));//true
1
2
3
4
5
6
7
//5. 判断定义为Stirng类型的str8、str9、str10是否相等
//str10 的地址值是堆内存的,而str9的地址值是常量池的
//变量和常量去串连:在StringBuffer[StringBuilder] 通过append()方法把元素添加进去,在通过toString转成String。
所以s3记录的地址值是ox222
String str8="ab";
String str9="abc";
String str10=str8+"c";
System.out.println(str10==str9);//fasle
System.out.println(str10.equals(str9)); //true
1
2
3
4
5
6
7
8
9
7>.字符串的常用方法 [ 掌握 ]
①. str.length():获取字符串长度(包括空格)
②. int indexOf(String s) : 返回s字符串在当前字符串中首次出现的位置。若没有,返回-1,下标从0开始
③. int lastIndexOf(String s):返回s字符串最后一次在当前字符串中出现的位置。若无,返回-1,下标从0开始
注意:如果lastIndexOf()方法参数中是空字符""(没有空格),那么结果和length()结果一样
④. char charAt(int index):获取指定索引位置的字符,下标从0开始
⑤. String subString():截取字符串,空格占用一个索 引位置
str.subString(int begin):从指定的索引位置截取到最后
str.subString(int begin,int end):从索引位置开始,包括前面,不包括后面
⑥. public String replace(charSequence oldString,charSequence newString):将所有出现的老字符串替换成新的字符串,返回替换之后的结果新字符串
charSequence的意思就是说可以接受字符串类型
⑦. 字母大小写转换
String toLowerCase():转小写
String toUpperCase():转大写
⑧. str.trim():去除两端的空格
⑨. String [ ] split(String regex):按照regex将当前字符串拆分,拆分为多个字符串,整体返回值为String [ ]
注意事项:split 方法的参数其实是一个 “正则表达式”; 今天要注意:如果要按照英文句点进行切分,必须写"\\."
⑩. public char[] toCharArray():将当前字符串拆分为字符数组作为返回值
11. public byte[] getBytes():获取当前字符串底层的字节数组
public byte[ ] getBytes(Charset charset):默认是utf-8
12. static String valueOf(char [ ] chs):将字符数组转换成字符串
13. static String valueOf(int i):将int类型的数据转成字符串;
注意:String类的valueOf()方法可以把任意类型的数据转成字符串
char[]chars="Hello".toCharArray();
//转换称为字符数组
for(int i=0;i<chars.length;i++){
System.out.print(chars[i]+"\t");//H e l l o
}
System.out.println("======");
//转换成字节数组
byte[]bytes="abc".getBytes();
for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i]+"\t");
}
System.out.println("======");
//把o换成*号
String str1="How do you do?";
String str2=str1.replace("o","*");
System.out.println("把o换成*号str2: "+str2);
System.out.println("======");
//String[]split()
String str3="aaa,bbb,ccc";
String[]str4=str3.split(",");
for(String str5:str4){
System.out.println(str5);
}
System.out.println("======");
String str6="aaa.bbb.ccc";
//String[]arrStr=str6.split(".");
String[]arrStr=str6.split("\\.");
for (int i = 0; i < arrStr.length; i++) {
System.out.println(arrStr[i]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//重点掌握
//1.byte[ ]getBytes():将字符串转换成字节数组
String s1="abc";
byte[]ch=s1.getBytes();
for (int i = 0; i < ch.length; i++) {
//System.out.print(ch[i]+"\t");
}
System.out.println("\n"+"--------");
String s2="你好你好";
//通过gbk码表将字符串转换成字节数组-- 编码的过程
//编码:把我们看的懂的转换成计算机看得懂的
//gbk中一个中文代表两个字节
//gbk码表的特点:中文的第一个字节,肯定是负数
byte[]arr2=s2.getBytes();
for (int i = 0; i < arr2.length; i++) {
//System.out.print(arr2[i]+"\t"); //97 98 99
}
//2.char[ ]toCharArray():把字符串转换成字符数组
char[]chs=s1.toCharArray();
for (int i = 0; i < chs.length; i++) {
//System.out.print(chs[i]+"\t");//a b c
}
//3.static String valueOf(char[]ch)
//底层是由String类的构造方法完成的
char[]ch1={'a','b','c'};
String str5=String.valueOf(ch1);
System.out.println("valueOf():"+str5);
String str6=String.valueOf(100);
System.out.println("将int 100 变成 字符串100:"+str6);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
8>. String类的判断功能
1. String类的判断功能
boolean equals(Object obj):比较字符串的内容是否相同,区分大小写
boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写写
boolean contains(String str):判断大字符串中是否包含小字符串
boolean startsWith(String str):判断字符串是否以某个指定的字符串开头
boolean endsWith(String str):判断字符串是否以某个指定的字符结束
boolean isEmpty(): 判断字符串是否为空
2. " " 和 null 的区别 [ 重点 ]
" "是字符串常量,也是一个String类的对象,既然是对象可以调用String类中的方法
null 是空常量,不能调用任何的方法,否则会出现空指针异常,null可以给任意的引用数据类型赋值
String s1="heima";
String s2="heima";
String s3="heiMa";
System.out.println(s2.equals(s3));//false
System.out.println(s2.equalsIgnoreCase(s3));//true
String s4="heimaxiaozhi";
boolean contains=s4.contains(s2);
System.out.println("s4中是否包含了s2的内容"+contains);
1
2
3
4
5
6
7
8
9
//重点知识点
System.out.println("-----");
String str1="heima";
String str2="";
String str3=null;
System.out.println(str1.isEmpty());//false
System.out.println(str2.isEmpty());//true
System.out.println(str3.isEmpty());//java.lang.NullPointerExceptioon
1
2
3
4
5
6
7
8
9>. String类的其他方法
String replace(char old , char new)
String replace(String old , String new)
String trim():String 类的去除字符串两端的空格 案例
int compareTo(String str):String的按字典顺序比较两个字符串及案例演示
int comareToIgnoreCase(String str)[了解 ]
String s = "heima";
String s2 = s.replace('e', 'x');
System.out.println(s2);//hxima
String s3 = s.replace('o', 'z');//z 不存在,保留原字符不改变
System.out.println(s3);//heima
String s4 = s.replace("ei", "aa");
System.out.println(s4);//haama
String s5 = " abc defg ";
String s6 = s5.trim();
System.out.println(s6);//abc defg
String s7 = "abc";
String s8 = "bcd";
int num = s7.compareTo(s8);
System.out.println(num); //-3 按照码表值比较
String s9 = "黑";
String s10 = "马";
int num2 = s9.compareTo(s10);
System.out.println(num2);
String s11 = "heima";
String s12 = "HEIMA";
int num3 = s11.compareToIgnoreCase(s12);
System.out.println(num3);//0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
10>. 遍历字符串 [ 掌握 ]
需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串
思路:①.键盘录入一个字符串,用Scanner实现
②.遍历字符串,首先要能够获取到字符串中的每一个字符
public char charAt(int index):返回指定索引出的char值,字符串的索引也是从0开始的
③.遍历字符串,其次要能够获取到字符串长度
public int length():返回字符串的长度
Scanner sc=new Scanner(System.in);
System.out.println("请随机输入一个字符串:");
String str=sc.nextLine();
for (int i = 0; i <str.length() ; i++) {
System.out.println(str.charAt(i));
}
1
2
3
4
5
6
String str="asdasfsa";
char[]chs=str.toCharArray();
for (int i = 0; i < chs.length; i++) {
System.out.print(chs[i]);
}
1
2
3
4
5
11>. 字符串练习
①. 统计字符次数
public static void main(String[] args) {
//键盘输入一个字符串,用Scanner实现
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
//要统计三中类型的字符个数,需要定义三个统计变量,初始值都是0
int bigCount=0;
int smallCount=0;
int numberCount=0;
int otherCount=0;
//遍历字符串,得到每一个字符
for (int i = 0; i < line.length(); i++) {
char ch=line.charAt(i);
//判断该字符属于哪种类型,然后对应类型的统计变量+1
if(ch>='A' && ch<='Z'){
bigCount++;
}else if(ch>='a' && ch<='z'){
smallCount++;
}else if(ch>='0'&&ch<='9' ){
numberCount++;
}else{
otherCount++;
}
}
System.out.println("大些字母:"+bigCount+"个");
System.out.println("小些字母:"+smallCount+"个");
System.out.println("数字有:"+numberCount+"个");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
②. 字符串反转
public class Demo4 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个字符串");
String str = sc.nextLine();
String strT=reverse(str);
System.out.println("反转后的顺序是:"+strT);
}
public static String reverse(String s){
//在方法中把字符串倒着遍历,然后把每一个得到的字符拼接成一个字符串并返回
String ss="";
for(int i=s.length()-1;i>=0;i--){
ss+=s.charAt(i);
}
return ss;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
③. 按照制定的格式拼接字符串
/*
定义一个方法,把数组{1,2,3} 按照制定格式拼接成一个字符串。参考按照如下:[word1#word2#word3]
格式:[word1#word2#word3]
用到: for 循环、字符串拼接、每个数组元素之前都有一个word字样、分割使用的是#、区分一下是不是最后一个
* */
public class DemoString {
public static void main(String[] args) {
int [] arr={1,2,3};
String strNew=fromArrayToString(arr);
System.out.println(strNew);
}
public static String fromArrayToString(int[]arr){
String str="[";
for(int i=0;i<arr.length;i++){
if(i==arr.length-1){
str+="word"+arr[i]+"]";
}else{
str+="word"+arr[i]+"#";
}
}
return str;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Test
public void fun3(){
/*
需求: 把数组中的数据按照指定的格式拼接成一个字符串
如: int[ ] arr={1,2,3};
输出结果: [1,2,3]
* */
int[ ] arr={1,2,3};
String str="[";
for (int i = 0; i < arr.length; i++) {
if(i==arr.length-1){
str+=arr[i]+"]";
}else {
str+=arr[i]+",";
}
}
System.out.println(str);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
④. 将一个字符串的首字母转成大写,其余为小写
// 链式编程 :只要保证每次调用完方法返回的是对象,就可以继续调用
// 将一个字符串的首字母转成大写,其余为小写
// 链式编程 :只要保证每次调用完方法返回的是对象,就可以继续调用
String str="aFDSFfdsrg";
String str2=str.substring(0,1).toUpperCase().concat(str.substring(1,str.length()).toLowerCase());
System.out.println("str2 = " + str2);
1
2
3
4
5
6
⑤. 常见对象(在大串中查找小串出现的次数代码实现)
//定义大串
String strMax = "woaiheima,heimabutongyubaodu,heimajiayou";
//定义小串
String strMin = "heima";
//定义计数器变量
int count = 0;
//定义索引
int index = 0;
//定义循环,判断小串是否在大串中出现
while ((index = strMax.indexOf(strMin)) != -1) {
count++; //计算器自增
strMax = strMax.substring(index + strMin.length());
}
System.out.println("小串在大串中出现的次数是" + count);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
⑥. 随机获取4个英文大写字母和一个数字
定义String getStr()方法
功能描述:
获取长度为5的随机字符串
字符串由随机的4个大写英文字母和1个0-9之间(包含0和9)的整数组成
英文字母和数字的顺序是随机的
1
2
3
4
5
//定义一个随机的数字看是否和循环时候是不是相同
public class StringTe {
public static void main(String[] args) {
String str=getStr5();
System.out.println("str = " + str);
}
public static String getStr5(){
//先随机的生成一个0-5之间的数据,如果种子是5,那么实际产生的数据就是0-4
Random r=new Random();
int j = r.nextInt(5);//就的数就是0-4中间的某一个
StringBuilder sb=new StringBuilder();
for(int i=0;i<5;i++){
if(i==j){
sb.append(r.nextInt(10));
}else {
//随机的产生大写字母字符
int bigNum = r.nextInt(26) + 65;
char ch=(char)bigNum;
sb.append(ch);
}
}
String s=sb.toString();
return s;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class StringDemoax {
public static void main(String[] args) {
String str=getStr5();
System.out.println("str = " + str);
}
public static String getStr5() {
StringBuilder sb=new StringBuilder();
Random r=new Random();
int randomNum=r.nextInt(10);
for(int i=0;i<5;i++){
if(i==randomNum/2){
sb.append(randomNum);
}else{
char ch=(char)(r.nextInt(26)+65);
sb.append(ch);
}
}
return sb.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
⑦.将第一个大写,其他的小写
已知字符串如下:
"good good study day day up"
请定义方法将字符串中每一个单词的首字母大写,其余的小写
最后打印的结果如下: (各位,千万别 sout("Good Good Study Day Day Up"); 哟)
"Good Good Study Day Day Up"
1
2
3
4
5
public class StringDemo {
public static void main(String[] args) {
String str="good good study day day up";
String string=getChange(str);
System.out.println("string = " + string);
}
private static String getChange(String str) {
StringBuilder sb=new StringBuilder();
//1.将字符串转成数组
String[] split = str.split(" ");
//2.遍历数组,将每一个字符
for (int i = 0; i < split.length; i++) {
//3.将字符串第一个字母大写,其他的转小写
String string=split[i].substring(0,1).toUpperCase().concat(split[i].substring(1).toLowerCase());
//4.创建StringBulider对象,把新的字符串添加进去
sb.append(string).append(" ");
}
//5.最后会有一个空格,最后把空格去掉
return sb.toString().trim();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static String change2(String str) {
StringBuilder sb=new StringBuilder();
String str2=" ";
int index=0;
while((index=str.indexOf(str2))!=-1){
String strN=str.substring(0,index);
sb.append(strN.substring(0,1).toUpperCase().concat(strN.substring(1).toLowerCase())).append(" ");
str=str.substring(index+1);
if(!str.contains(str2)){
sb.append(str.substring(0,1).toUpperCase().concat(str.substring(1).toLowerCase()));
}
}
return sb.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2. StirngBuffer
1>. StringBuffer的概述
java.lang.StringBuffer代表可变的字符序列,线程安全可变的字符序列 [ 线程安全,所以效率低 ]
很多方法与String相同,但StingBuffer是可变长度的。
StringBuffer是一个容器。
2>. StringBuffer的构造方法
public StringBuffer():无惨构造方法
public StringBuffer(String str):指定字符串内容的字符串缓冲区对象
public StringBuffer(int capacity):指定容量的字符缓冲区对象
public int capacity():返回当前容量 [ 理论值 ]
public int length( ):返回长度(字符数) [ 实际值 ]
StringBuffer sb = new StringBuffer();
System.out.println(sb.length());//0
System.out.println(sb.capacity());//容器初始容量,理论值 16
StringBuffer sb2 = new StringBuffer("xiaozhi");
System.out.println(sb2.length());//7
System.out.println(sb2.capacity());//16+7=23
1
2
3
4
5
6
7
3>. StringBuffer 的添加功能
public StringBuffer append(String str):可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身
public StringBuffer insert(int offset,String str):在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
StirngBuffer 是将字符串缓冲区,当new 的时候是在堆内存创建了一个对象, 底层是一个长度为16的字符数组,当调用添加方法时,不会再创建对象,在不断向原缓冲区添加字符
StringBuffer str=new StringBuffer("xiaozhi");
StringBuffer str2=str.append("6");
System.out.println(str2);//xiaozhi6
StringBuffer str3=str.insert(0,false);
System.out.println(str3);//falsexiaozhi6
1
2
3
4
5
4>. StringBuffer的删除功能 [ 了解 ]
public StringBuffer deleteCharAt(int index) : 删除指定位置的字符,并返回本身
public StringBuffer delete(int start,int end):删除从指定位置开始指定位置结束的内容,并返回本身[ )
StringBuffer sb=new StringBuffer("xiaozhi");
//删除a这个元素
sb.deleteCharAt(2);
System.out.println(sb);//xiozhi
//删除o这个字符
sb.delete(2,3);
System.out.println(sb);//xizhi
//清空缓冲区
sb.delete(0,sb.length());
System.out.println("清空缓冲区"+sb);
1
2
3
4
5
6
7
8
9
10
5>. StringBuffer的替换和反转功能
public StringBuffer replace(int start,int end,String str):从start开始到end用str替换
public StringBuffer reverse():反转
StringBuffer sb=new StringBuffer("heima");
sb.replace(0,3,"ttt");
System.out.println(sb);//tttma
sb.reverse();
System.out.println("反转后的顺序是:"+sb);//amttt
1
2
3
4
5
6
6>. StringBuffer 的截取功能
public String subString(int start):从指定位置截取到末尾
public String subString(int start,int end):截取从指定位置开始到结束位置,包括开始位置,不包括结束位置
注意事项:返回值类型不再是StringBuffer,要拿String去接收
StringBuffer sb=new StringBuffer("xiaozhi");
//截取zhi
String str1=sb.substring(4);
System.out.println("str1 = " + str1);
//截取xiao
String str2=sb.substring(0,4);
System.out.println("str2 = " + str2);
1
2
3
4
5
6
7
7>. StringBuffer 和 String相互转换 [ 掌握 ]
1. String — StringBuffer
①. 通过带参构造方法
②. 通过无参构造方法 和 通过append()方法
//通过构造方法将字符串转换成StringBuffer对象
StringBuffer sb=new StringBuffer("heima");
System.out.println(sb);//这是一个StringBuffer对象了
StringBuffer sb2=new StringBuffer();
sb2.append("heima");
System.out.println(sb2);
1
2
3
4
5
6
2. StringBuffer – String
①. 通过构造方法
②. 通过toString()方法
③. 通过subString(0,length)
String s1=new String(sb2);
String s3=sb2.toString();
String s4=sb2.substring(0,sb2.length());
1
2
3
3. StringBuilder
1>.StringBuilder的概述
StringBuilder 是一个可变的字符串类,我们可以把它看成一个容器
String和 StringBuilder的区别
String:内容是不可变的
StringBuilder:内容是可变的
2>. StringBuilder的构造方法
public StringBuilder():创建一个空白可变字符串对象,不含有任何内容
public StringBuilder(String str):根据字符串的内容,来创建可变字符串对象
3>. StringBuilder的添加和反转方法
public StringBuilder append(任意类型):添加数据,并返回对象本身
public StringBuilder reverse():返回相反的字符序列
4>. StringBuilder 和String 相互转换
①. StringBuilder 转换为String
public String toString():通过toString()就可以实现把StringBuilder转换为String
②. String 转换为 StringBuilder
public StringBuilder(String s):通过构造方法就可以现在把String 转换为StringBuilder
//StringBuffer-->string
StringBuilder sbf=new StringBuilder();
sbf.append("hello");
String str = sbf.toString();
System.out.println(str);
//String-->StringBuffer
String str1="helloStr";
StringBuilder sb=new StringBuilder(str1);
System.out.println(sb);
1
2
3
4
5
6
7
8
9
10
public class Demo4 {
public static void main(String[] args) {
//定义一个int类型的数组,用静态初始化完成数组元素的初始化
int[]arr=new int[]{1,2,3};
String strT=arrayToString(arr);
System.out.println("输出的结果是:"+strT);
}
public static String arrayToString(int[]arr){
//在方法中用StringBuilder 按照要求进行拼接,并把结果转成String返回
StringBuilder sb=new StringBuilder();
sb.append("[");
for (int i = 0; i <arr.length ; i++) {
if(i==arr.length-1){
sb.append(arr[i]);
}else{
sb.append(arr[i]).append(",");
}
}
sb.append("]");
String s=sb.toString();
return s;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
4. StirngBuffer 、StringBuilder 和 String
StirngBuffer 和 StringBuilder 是一样的
1. StringBuffer 和 StringBuilder 的区别
StirngBuffer 是jdk1.0版本的,是线程安全的,效率低
StringBuilder 是jdk1.5 版本的,是线程不安全的,效率高
2. StirngBuffer 、StringBuilder 和 String 的区别
String是一个可变的字符序列
StirngBuffer 、StringBuilder 是可变的字符序列
5. StringBuilder的扩容 [ 掌握 ]
①.第一种方式:空惨构造
public class Capacity1 {
public static void main(String[] args) {
System.out.println("-----------第一种-----------");
//可变的字符序列
//StringBuilder() 构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符。
StringBuilder sb = new StringBuilder();
// int length() 返回长度(字符数)
System.out.println("实际存储字符的个数:"+sb.length()); //0
// int capacity() 返回当前容量。
System.out.println("容量"+sb.capacity()); //16
System.out.println("----------添加数据-----------");
sb.append("aaaaaaaaaaaaaaa"); //字符串的长度是15
System.out.println("实际存储字符的个数:"+sb.length()); //15
System.out.println("容量"+sb.capacity()); //16
sb.append("a");
System.out.println("----------当SB的长度为15的时候,又添加了一个字符----------");
System.out.println("实际存储字符的个数:"+sb.length()); //16
System.out.println("容量"+sb.capacity()); //16
sb.append("a");
System.out.println("----------当SB的长度为16的时候,又添加了一个字符----------");
System.out.println("实际存储字符的个数:"+sb.length()); //17
System.out.println("容量"+sb.capacity()); //34 ? 几倍
//int newCapacity = (value.length << 1) + 2; // 原来容器数据的长度 * 2 + 2
//value.length << 1 相当于 16 * 2
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
②. StringBuilder(int capacity):在创建sb的时候直接指定容量,一般很少用
System.out.println("-----------第二种--------------");
//StringBuilder(int capacity)
//在创建SB的时候直接指定容量,一般很少用
StringBuilder sb1 = new StringBuilder(30);
System.out.println("容量"+sb1.capacity()); //30
System.out.println("长度"+sb1.length()); //0
1
2
3
4
5
6
③. 在构造方法中添加字符串
System.out.println("-----------第三种--------------");
StringBuilder sb2 = new StringBuilder("str");
/*
扩容的源码:super(str.length() + 16);
根据构造方法中添加元素的长度+ 默认的容量 == 19
*/
System.out.println("容量"+sb2.capacity()); //19
System.out.println("长度"+sb2.length()); //3
1
2
3
4
5
6
7
8
异常
1>. 异常的概述和分类
2>. Jvm默认是如何处理异常的
①. Jvm有一个默认的异常处理机制,就将该异常进行处理。并将该异常的名称,异常的信息,异常出现的位置打印在了控制台上
②. 同时将程序停止运行
//看异常从后往前看,先看这里的 java:6 再看 java:14
public class JvmException {
public static void main(String[] args) {
DemoJvm d=new DemoJvm();
int x=d.div(1,0);
System.out.println(x);//ArithmeticException: / by zero
}
}
class DemoJvm{
public int div(int a,int b){//a=10 b=0
// 10/0 被除数是10,除数是0当除数是0的时候违背了算数运算法则,抛出异常
// new ArithmeticExcpetion("/ by zero")
return a/b;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
3>. try–catch的方式处理异常
try:用来检测异常的;catch:用来捕获异常的;finally:用来释放资源的
异常处理的两种方式
try.....catch.....finally
1.try.....catche
2.try.....catche.....finally
3.try.....finally
throws
1
2
3
4
5
6
7
世界上最真情的相依就是你在try 我在catch,无论你发神马脾气,我都静静接受,默默处理
①.当通过trycatch将问题处理了,程序会继续执行
②. try后面如果跟多个catch,那么小的异常放前面,大的异常放后面,根据多肽的原理,如果大的放前面,就会将所有的子类对象接受,后面的catch就没有意义了
4>. 编译期异常和运行期异常的区别
所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常
编译时异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译
运行时异常:无需显示处理,也可以和编译时异常一样处理
5. Throwable的集中常见方法
String getMessage():获取异常信息,返回字符串
String toString():获取异常类和异常信息,返回字符串
void printStackTrace():获取异常类名和异常信息,以及异常出现在程序中的位置,返回值为void [ Jvm 默认这种方式 ]
try{
System.out.println(1/0);
}catch (Exception e){
//Exception e =new ArithmeticException("/ by zero");
System.out.println(e.getMessage());//获取异常信息 / by zero
//toString():打印异常类名和异常信息
System.out.println(e.toString());//java.lang.ArithmeticException: / by zero
System.out.println(e);//java.lang.ArithmeticException: / by zero
//printStackTrance():JVM 默认这样方式处理
e.printStackTrace();
//at day08.ExcepetionDemo.main(ExcepetionDemo.java:8)
//java.lang.ArithmeticException: / by zero
}
1
2
3
4
5
6
7
8
9
10
11
12
13
6>. throws的方式处理异常
①. 编译时异常的抛出,必须对其进行处理
②. 运行时异常的抛出,可以处理也可以不处理
throw:在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出
//掌握
throws:
1. 用在方法声明后面,跟的是异常类名
2. 可以跟多个异常类名,用逗号隔开
3. 表示抛出异常,由该方法的调用者来处理
throw:
1. 用在方法体内,跟的是异常对象名
2. 只能抛出一个异常类对象名
3. 表示抛出异常,由方法体内的语句处理
Exception e=new Exception("年龄非法");
throw e;
1
2
3
4
5
6
7
8
9
10
11
7>. finally
1. finally的特点
被finally控制的语句体一定会执行
特殊情况:在执行到finally之前jvm退出了(比如:System.exit(0))
//return语句相当于是方法的最后一口气,那么在它将死之前会看一
//看有没有finally帮其完成遗愿。如果有就将finally执行后切底返回
try{
System.out.println(10/0);
}catch (Exception e){
System.out.println("除数为0了");
return;
}finally {
System.out.println("一定会执行的");
}
1
2
3
4
5
6
7
8
9
10
2. 面试题 [ 掌握 ]
1.final,finally和finalize的区别
final:
final可以修饰类,不能为继承
final修饰方法,不能被重写
final修饰变量,只能赋值一次
finally:
finally是try语句中的一个语句体,不能单独使用,用来释放资源
finalize:
是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,
由对象的垃圾回收器调用次方法
2.如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问在return前还是retrun后
会执行,在return前执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
8>. 自定义异常概述和基本使用
1. 异常的注意事项
①. 子类重写父类方法时,要求子类抛出的异常要小于父类抛出的异常
②. 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内部有异常发生,那么子类只能try,不能throws
2. 如何使用异常处理
原则:如果该功能类部可以将问题处理,用try,如果处理不了,交由调用者处理,这里用throws
//Exception是编译时异常
//RuntimeException时运行时异常
public class ScoreException extends Exception/*RuntimeException*/ {
public ScoreException(){
}
public ScoreException(String message){
super(message);
}
}
1
2
3
4
5
6
7
8
9
public class Teacher {
public void checkScore(int score)throws ScoreException{
if(score<0||score>100){
//throw new ScoreException();
throw new ScoreException("你输入的数字有误");
}else{
System.out.println("正确");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Demo {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入你的成绩:");
int score=sc.nextInt();
Teacher t=new Teacher();
try {
t.checkScore(score);
} catch (ScoreException e) {
e.printStackTrace();
}
}
}