StringBuffer的功能
A:StringBuffer的添加功能
public StringBuffer append(String str):
可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身
//在堆内存创建了一个对象
StringBuffer sb = new StringBuffer();
//sb1,sb2指向的地址都是同一个(变长),这个要区别String类定长的概念
StringBuffer sb1 = sb.append(true);
StringBuffer sb2 = sb.append(“luoshijie”);
System.out.println(sb1);//trueluoshijie
System.out.println(sb2);trueluoshijie
/**解释:StringBuffer是字符串缓冲区,当new的时候是在堆内存创建了一个对象,底层是一个长度为16的字符数组
当调用添加的方法时,不会再重新创建对象,在不断向原缓冲区添加字符*/
- public StringBuffer insert(int offset,String str):
- 在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
注意:1.当缓冲区中这个索引上没有元素的时候就会报StringIndexOutOfBoundsException
2.任意类型的数据插入指定索引的时候,原来索引的数据会向后移
- 在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
B:StringBuffer的删除功能
public StringBuffer deleteCharAt(int index):
删除指定位置的字符,并返回本身
public StringBuffer delete(int start,int end):
删除从指定位置开始指定位置结束的内容,并返回本身(删除的时候是包含头,不包含尾)
注意:当缓冲区中这个索引上没有元素的时候就会报
StringIndexOutOfBoundsException
sb = new StringBuffer(); // 不要用这种方式清空缓冲区,原来的会变成垃圾,浪费内存
sb.delete(0, sb.length()); // 清空缓冲区
C:StringBuffer的替换功能
public StringBuffer replace(int start,int end,String str):
从start开始到end用str替换
D:StringBuffer的反转功能
public StringBuffer reverse():
字符串反转
E:StringBuffer的截取功能
:注意事项
注意:返回值类型不再是StringBuffer本身
public String substring(int start):
从指定位置截取到末尾
public String substring(int start,int end):
截取从指定位置开始到结束位置,包括开始位置,不包括结束位置
注意:
//错误做法,这样会导致arr[i]+" ," 在堆内存创建一个StringBuffer(StringBuilder)对象,
//接着该对象调用toString()方法转为String类(此时该String类对象在堆内存中),极大浪费内存
sb.append(arr[i] + ", ");
//正确做法
sb.append(arr[i]).append(", ");
String s1="a"+"b";//这种都是在常量池中进行,因为"a","b"都是常量,jvm有常量优化机制
String s2="a"+sb;//这种有变量参与了运算了,所以此时需要到堆内存中搞了,参考arr[i]+" ,"
StringBuffer和StringBuilder的区别
StringBuffer是jdk1.0版本的,是线程安全的(安全就是同步的),效率低
StringBuilder是jdk1.5版本的,是线程不安全的(安全就是不同步的),效率高
String,StringBuffer,StringBuilder的区别
String是一个不可变的字符序列
StringBuffer,StringBuilder是可变的字符序列
Java中参数方法问题:
1.基本数据类型的值传递,不改变其值
2.引用数据类型的值传递,改变其值
3.String类虽然是引用数据类型,但是他当作参数传递时和基本数据类型是一样的,但StringBuffer,StringBuilder作为值传递,会改变其值
Arrays类的概述和方法使用
A:Arrays类概述
针对数组进行操作的工具类。
提供了排序,查找等功能。
B:成员方法
public static String toString(int[] a)//数组转字符串
public static void sort(int[] a)//排序
public static int binarySearch(int[] a,int key)//二分查找,找不到返回-插入点-1
基本类型包装类的概述
A:为什么会有基本类型包装类
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。
B:常用操作
常用的操作之一:用于基本数据类型与字符串之间的转换。
C:基本类型和包装类的对应
String和int类型的相互转换)
A:int – String
a:和”“进行拼接
b:public static String valueOf(int i)
c:int – Integer – String(Integer类的toString方法())
d:public static String toString(int i)(Integer类的静态方法)
int i = 100;
String s1 = i + “”; //推荐用
String s2 = String.valueOf(i); //推荐用
Integer i2 = new Integer(i);
String s3 = i2.toString();
String s4 = Integer.toString(i);
B:String – int
1.基本数据类型包装类有八种,其中七种都有parseXxx的方法(Character中没有),可以将这七种的字符串表现形式转换成基本数据类型
2.字符串String到字符char的转换通过toCharArray()就可以把字符串转换为字符数组
a:String – Integer – int
public static int parseInt(String s)
//String----> int String 转换int
String s = “200”;
Integer i3 = new Integer(s);
int i4 = i3.intValue(); //将Integer转换成了int数
int i5 = Integer.parseInt(s); //将String转换为int,推荐用这种
JDK5的新特性自动装箱和拆箱
A:JDK5的新特性
自动装箱:把基本类型转换为包装类类型
自动拆箱:把包装类类型转换为基本类型
B:案例演示
System.out.println(“JDK1.5之前的做法:”);
int x = 100;
Integer i1 = new Integer(x); //将基本数据类型包装成对象,装箱
int y = i1.intValue(); //将对象转换为基本数据类型,拆箱
System.out.println("JDK1.5之后的做法:");
Integer i2 = 100; //自动装箱,把基本数据类型转换成对象
int z = i2 + 200; //自动拆箱,把对象转换为基本数据类型
System.out.println(z);
Integer i3 = null;
int a = i3 + 100; //底层用i3调用intValue,但是i3是null,null调用方法就会出现
C:注意事项
在使用时,Integer x = null;代码就会出现NullPointerException。
建议先判断是否为null,然后再使用。
D:面试题
Integer i1 = new Integer(97);
Integer i2 = new Integer(97);
System.out.println(i1 == i2); // false
System.out.println(i1.equals(i2)); // true
System.out.println("-----------");
Integer i3 = new Integer(197);
Integer i4 = new Integer(197);
System.out.println(i3 == i4); // false
System.out.println(i3.equals(i4)); // true
System.out.println("-----------");
Integer i5 = 127;
Integer i6 = 127;
System.out.println(i5 == i6); // true
System.out.println(i5.equals(i6)); // true
System.out.println("-----------");
Integer i7 = 128;
Integer i8 = 128;
System.out.println(i7 == i8);// false
System.out.println(i7.equals(i8)); // true
/*
* -128到127是byte的取值范围,如果在这个取值范围内,自动装箱就不会新创建对象,而是从常量池中获取
* 如果超过了byte取值范围就会再新创建对象
*
* public static Integer valueOf(int i) { assert IntegerCache.high >=
* 127; if (i >= IntegerCache.low && i <= IntegerCache.high) //i>= -128
* && i <= 127 return IntegerCache.cache[i + (-IntegerCache.low)];
* return new Integer(i); }
*/
注意:自动装箱规范要求boolean,byte,char<=127(ASCII码),介于-128~127之间的short和int会被自动包装到固定的对象中.
正则表达式
正则表达式是指一个用来描述或者匹配一系列符合某个语法规则的字符串的单个字符串。其实就是一种规则。有自己特殊的应用。
A:字符类
[abc] a、b 或 c(简单类),其实[]代表单个字符
[^abc] 任何的单个字符,除了 a、b 或 c(否定)
[a-zA-Z] a到 z 或 A到 Z,两头的字母包括在内(范围)
[0-9] 0到9的字符都包括
B:预定义字符类
. 任何单个字符。
\d 数字:[0-9]
\w 单词字符:[a-zA-Z_0-9]
C:Greedy 数量词
X? X,一次或一次也没有
X* X,零次到多次
X+ X,一次到多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
其中需要注意的是:有多重意思:正则表达式中的””这个符号的一些思考 - 风烟 - 博客频道 - CSDN.NET
正则表达式的分割功能
A:正则表达式的分割功能
String类的功能:public String[] split(String regex)//通过正则表达式切割字符串
Pattern和Matcher的概述
经典例子:
Pattern p = Pattern.compile("a*b"); //获取到正则表达式
Matcher m = p.matcher("aaaaab"); //获取匹配器
boolean b = m.matches(); //看是否能匹配,匹配就返回true
System.out.println(b);
System.out.println("aaaaab".matches("a*b")); //与上面的结果一样
面试题: 把一个字符串中的手机号码获取出来
String s = "我的手机是18988888888,我曾用过18987654321,还用过18812345678";
String regex = "1[3578]\\d{9}";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
boolean b1 = m.find();
System.out.println(b1);
//必须调find()方法来找才可以调用group
System.out.println(m.group());//18988888888
//进行找的时候,找到一个并会再次调用的时候,
//便会从之前找到的位置继续向下找,一直到最后
boolean b2 = m.find();
System.out.println(b2);
//必须调find()方法来找才可以调用group
System.out.println(m.group());//18987654321
while(m.find())
System.out.println(m.group());
BigDecimal类的概述和方法使用
A:BigDecimal的概述
由于在运算的时候,float类型和double很容易丢失精度,演示案例。
所以,为了能精确的表示、计算浮点数,Java提供了BigDecimal
不可变的、任意精度的有符号十进制数。
B:构造方法
public BigDecimal(String val)
C:成员方法
public BigDecimal add(BigDecimal augend)
public BigDecimal subtract(BigDecimal subtrahend)
public BigDecimal multiply(BigDecimal multiplicand)
public BigDecimal divide(BigDecimal divisor)
开发中需要准确的数据,而不是接近精确的数据,如下所示:
//System.out.println(2.0 - 1.1);
BigDecimal bd1 = new BigDecimal(2.0); //这种方式在开发中不推荐,因为不够精确
BigDecimal bd2 = new BigDecimal(1.1);
System.out.println(bd1.subtract(bd2));
System.out.println("------------------------");
BigDecimal bd3 = new BigDecimal("2.0"); //通过构造中传入字符串的方式,开发时推荐
BigDecimal bd4 = new BigDecimal("1.1");
System.out.println(bd3.subtract(bd4));
System.out.println("------------------------");
BigDecimal bd5 = BigDecimal.valueOf(2.0); //这种方式在开发中也是推荐的
BigDecimal bd6 = BigDecimal.valueOf(1.1);
System.out.println(bd5.subtract(bd6));
Date类的概述和方法使用(掌握)
A:Date类的概述
类 Date 表示特定的瞬间,精确到毫秒。
B:构造方法
public Date()//如果没有传参数代表的是当前时间(从当前的系统获得)
public Date(long date)//如果构造方法中参数传为0代表的是1970年1月1日
C:成员方法
public long getTime()//跟System.currentTimeMillis()一样
public void setTime(long time)
SimpleDateFormat类实现日期和字符串的相互转换(掌握)
A:DateFormat类的概述
DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。是抽象类,所以使用其子类SimpleDateFormat
B:SimpleDateFormat构造方法
public SimpleDateFormat() public SimpleDateFormat(String pattern)
Date d = new Date(); //获取当前时间对象
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy/MM/dd HH:mm:ss”);//创建日期格式化类对象
System.out.println(sdf.format(d));
C:成员方法
public final String format(Date date) public Date parse(String source)
//将时间字符串转换成日期对象
String str = “2000年08月08日 08:08:08”;
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm:ss”);
Date d = sdf.parse(str);//将时间字符串转换成日期对象
System.out.println(d);
C 算一下你来到这个世界多少天?(假设你是1983年07月08日出生,现在是2088年6月6日)
//1,将生日字符串和今天字符串存在String类型的变量中
String birthday = “1983年07月08日”;
String today = “2088年6月6日”;
//2,定义日期格式化对象
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日”);//如果上面用/,那么这里也要用/
//3,将日期字符串转换成日期对象
Date d1 = sdf.parse(birthday);
Date d2 = sdf.parse(today);
//4,通过日期对象后期时间毫秒值
long time = d2.getTime() - d1.getTime();//d2.getcurrent
//5,将两个时间毫秒值相减除以1000,再除以60,再除以60,再除以24得到天
System.out.println(time / 1000 / 60 / 60 / 24 );
对象数组的概述和使用
A:案例演示
需求:我有5个学生,请把这个5个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息。
Student[] arr = new Student[5]; //存储学生对象,是一个JavaBean类,里面有String name跟int age属性 arr[0] = new Student("张三", 23); arr[1] = new Student("李四", 24); arr[2] = new Student("王五", 25); arr[3] = new Student("赵六", 26); arr[4] = new Student("马哥", 20);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
图解:
数组和集合存储引用数据类型,存的都是地址值
集合的由来及集合继承体系图
-
A:集合的由来
数组长度是固定,当添加的元素超过了数组的长度时需要对数组重新定义,太麻烦,java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,随着元素的增加而增加,随着元素的减少而减少 -
B:数组和集合的区别 区别1 :
数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值
集合只能存储引用数据类型(对象),集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象 区别2:
数组长度是固定的,不能自动增长 集合的长度的是可变的,可以根据元素的增加而增长 -
C:数组和集合什么时候用
1,如果元素个数是固定的推荐用数组
2,如果元素个数不是固定的推荐用集合
D:集合继承体系图
- 数据结构之数组和链表
-
A:数组
1查询快修改也快 2增删慢
-
B:链表
1查询慢,修改也慢
2增删快
List的三个子类的特点
- A:List的三个子类的特点
- ArrayList:底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。- Vector: 底层数据结构是数组,查询快,增删慢。 线程安全,效率低。 Vector相对ArrayList查询慢(线程安全的) Vector相对LinkedList增删慢(数组结构) - LinkedList 底层数据结构是链表,查询慢,增删快。 线程不安全,效率高。
Vector和ArrayList的区别
Vector是线程安全的,效率低
ArrayList是线程不安全的,效率高
共同点
***:都是数组实现的
ArrayList和LinkedList的区别
ArrayList底层是数组结果,查询和修改快
LinkedList底层是链表结构的,增和删比较快,查询和修改比较慢
共同点:都是线程不安全的
B:List有三个儿子,我们到底使用谁呢?
查询多用ArrayList(开发中用的多一点)
增删多用LinkedList
contains方法判断是否包含,底层依赖的是equals方法
remove方法判断是否删除,底层依赖的是equals方法
泛型概述和基本使用(掌握)
- A:泛型好处
提高安全性(将运行期的错误转换到编译期)
省去强转的麻烦(如果集合不加泛型,默认存储的是Object类,即是存储进来的都会事先转为Object)
- B:泛型基本使用
<>中放的必须是引用数据类型
-
C:泛型使用注意事项
- 前后的泛型必须一致,或者后面的泛型可以省略不写(1.7的新特性菱形泛型)
- 集合了指明的泛型类之后,迭代器Iterator就必须是该泛型类(该泛型类的父类以及子类都不可以),一句话:指定了什么类型就是什么类型,他父亲儿子都不可以替代他
- 方法泛型最好与类的泛型一致(这样与类泛型相同的类内方法可以不写泛型了),如果不一致,需要在方法(静态方法必须声明自己的泛型,写在static关键字之后)上声明该泛型,格式:public
<泛型类型> 返回类型 方法名(泛型类型 变量名),如下所示:
public<T> void show(T t) { //方法泛型最好与类的泛型一致 System.out.println(t); //如果不一致,需要在方法上声明该泛型 } public static<W> void print(W w) { //静态方法必须声明自己的泛型 System.out.println(w); /**假设该静态方法所在类的泛型也是W,但这两个W代表的类型可能不一样,类的泛型是在创建对象时候才有,而静态方法的泛型是在类名或者类对象调用该静态方法的时候有(调用该方法的时候必须要指明是什么泛型)*/ }
泛型高级之通配符(了解)
A:泛型通配符
三种迭代的能否删除(掌握)
- 普通for循环,可以删除,但是索引要–(如果删除的时候索引不进行减1的话,但如果有两个相邻的该被删的对象,只会删除索引值最前的一个)
- 假如删除时候索引不减少1的分析:
如果下图所示,当i=1的时候,符合if条件,list.remove移除了索引为1的”b”,此时候下面的元素的索引值就会自动减少1(自己自动前进1位),这样就导致索引为2的”b”移到了索引为了1的位置,逃避了被移除的危险
所以应该改为list.remove(i–);不需要担心它会为-1,因为i++也在,当其真的变成-1那一瞬间i++也会执行的
- 迭代器,可以删除,但是必须使用迭代器自身的remove方法,否则会出现并发修改异常
Iterator<String> it = list.iterator();//ArrayList<String> list = new ArrayList<>(); while(it.hasNext()) { if("b".equals(it.next())) { //list.remove("b"); //不能用集合的删除方法,因为迭代过程中如果集合修改会出现并发修改异常 it.remove();//只有删除方法.没有增加方法 } }
list.iterator();迭代器只有移除的方法;如果还需要增加方法,需要用到list.listIterator();(这种迭代器增加删除方法都有)
- 增强for循环不能删除
可变参数的概述和使用(掌握)
-
A:可变参数概述
- 定义方法的时候不知道该定义多少个参数
-
B:格式
- 修饰符 返回值类型 方法名(数据类型… 变量名){}
-
C:注意事项:
- 这里的变量其实是一个数组 如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是最后一个
public class Demo3_ChangeableArgs {
public static void main(String[] args) { int[] arr = {11,22,33,44,55}; //print(arr); print(11,22,33,44,55);//使用可变参数之后,可以写成这样 System.out.println("---------------"); //print(); } /*public static void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } }*/ //可变参数其实是一个数组,跟上面的方法是一样的 public static void print(int ... arr) { for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }
数组与集合List之间的转换
-
A Arrays工具类的asList()方法的使用
1.数组转换成集合虽然不能增加或减少元素,但是可以用集合的思想操作数组(比如可以使用集合contains之类的),也就是说可以使用其他集合中的方法
String[] arr = {"a","b","c"}; List<String> list = Arrays.asList(arr); //将数组转换成集合
2.基本数据类型的数组转换成集合,会将整个数组当作一个对象转换;将数组转换成集合,数组必须是引用数据类型
//基本数据类型的数组转换成集合,会将整个数组当作一个对象转换
int[] arr0 = {11,22,33,44,55};
List<int[]> list0 = Arrays.asList(arr0);//[[I@15db9742]
System.out.println(list0);
//将数组转换成集合,数组必须是引用数据类型
Integer[] arr1 = {11,22,33,44,55};
List<Integer> list1 = Arrays.asList(arr1);
System.out.println(list1);//[11, 22, 33, 44, 55]
-
B Collection中toArray(T[] a)泛型版的集合转数组
ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); /**集合转换数组时,数组长度如果是小于等于集合的size时, 转换后的数组长度等于集合的size,如果数组的长度大于了size, 分配的数组长度就和你指定的长度一样*/ String[] arr = list.toArray(new String[10]); for (String string : arr) { System.out.println(string); }
HashSet存储字符串并遍历
Set集合,无索引,不可以重复,无序(存取不一致)
HashSet存储自定义对象保证元素唯一性
案列:
//其中Person是一个Bean类型,主要用String name跟int age两个属性
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三", 23));
hs.add(new Person("张三", 23));
hs.add(new Person("李四", 23));
hs.add(new Person("李四", 23));
hs.add(new Person("王五", 23));
hs.add(new Person("赵六", 23));
分析:如果按照上面来搞,hs最终会存储6个Person引用对象(根据地址值不同),如果认定名字跟年龄都相同的是一个对象,那么就需要重写hashCode()和equals()方法
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
//HashCode方法可以用eclipse生成.Alt+Shift+S
@Override
public int hashCode() {
return 10;
// return super.hashCode();
}
//equals方法可以用eclipse生成.Alt+Shift+S
@Override
public boolean equals(Object obj) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
当你把对象往HashSet里面存储的时候,就会调用Person类中的HashCode()方法(该方面是Object类的)进行比较,只有HashCode方法返回的值一样的时候才会自动调用equals()方法进行比较,equals()返回true就是同一个对象,false就是不同对象
HashSet如何保证元素唯一性的原理
- 1.HashSet原理
- 我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率,
降低了使用equals()方法的次数 - 当HashSet调用add()方法存储对象的时候, 先调用对象的hashCode()方法得到一个哈希值,
然后在集合中查找是否有哈希值相同的对象 - 如果没有哈希值相同的对象就直接存入集合
- 如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行equals()比较,比较结果为false就存入, true则不存
- 我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率,
- 2.将自定义类的对象存入HashSet去重复
- 类中必须重写hashCode()和equals()方法
- hashCode(): 属性相同的对象返回值必须相同, 属性不同的返回值尽量不同(提高效率)
- equals(): 属性相同返回true, 属性不同返回false,返回false的时候存储
/* * 为什么是31? * 1,31是一个质数,质数是能被1和自己本身整除的数 * 2,31这个数既不大也不小 * 3,31这个数好算,2的五次方-1,2向左移动5位 */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) //调用的对象和传入的对象是同一个对象 return true; //直接返回true if (obj == null) //传入的对象为null return false; //返回false if (getClass() != obj.getClass()) //判断两个对象对应的字节码文件是否是同一个字节码 return false; //如果不是直接返回false Person other = (Person) obj; //向下转型 if (age != other.age) //调用对象的年龄不等于传入对象的年龄 return false; //返回false if (name == null) { //调用对象的姓名为null if (other.name != null) //传入对象的姓名不为null return false; //返回false } else if (!name.equals(other.name)) //调用对象的姓名不等于传入对象的姓名 return false; //返回false return true; //返回true }
LinkedHashSet的概述和使用
底层是链表实现的,是set集合中唯一一个能保证怎么存就怎么取的集合对象,因为是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样
LinkedHashSet的特点:可以保证怎么存就怎么取
TreeSet
TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一
如果是自定义对象,比如存储的一个Person的Bean对象,代码如下所示,Comparable中实现了Integer,String等,对于自定义的Person,TreeSet就不知道按照什么要求(姓名还是年龄?)来存储了
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("张三", 23));
ts.add(new Person("李四", 13));
ts.add(new Person("周七", 13));
ts.add(new Person("王五", 43));
ts.add(new Person("赵六", 33));
System.out.println(ts);
解决办法:继承Comparable接口,接着重写compareTo方法:
//你需要比较什么就Comparable<T>泛型就传什么 public class Person implements Comparable<Person> { private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } ... public int compareTo(Person o) { return 0 ;//当compareTo方法返回0的时候集合中只有一个元素 } ... }
改进代码:
如果你仅仅比较年龄的话,当年龄相等的话,那么即使不是同一个人,都不能存进去,所以需要加多几个条件
public int compareTo(Person o) {
int length = this.name.length() - o.name.length(); //比较长度为主要条件
int num = length == 0 ? this.name.compareTo(o.name) : length; //比较内容为次要条件
return num == 0 ? this.age - o.age : num; //比较年龄为次要条件
}
TreeSet原理
- 1.特点
TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列
- 2.使用方式
- a.自然顺序(Comparable) TreeSet类的add()方法中会把存入的对象提升为Comparable类型
调用对象的compareTo()方法和集合中的对象比较 根据compareTo()方法返回的结果进行存储 - b.比较器顺序(Comparator) 创建TreeSet的时候可以制定 一个Comparator
如果传入了Comparator的子类对象, 那么TreeSet就会按照比较器中的顺序排序
add()方法内部会自动调用Comparator接口中compare()方法排序
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参
数 - c.两种方式的区别 TreeSet构造函数什么都不传,
默认按照类中Comparable的顺序(没有就报错ClassCastException) TreeSet如果传入Comparator,
就优先按照Comparator
- a.自然顺序(Comparable) TreeSet类的add()方法中会把存入的对象提升为Comparable类型
总结:
- 1.List
- a.普通for循环, 使用get()逐个获取
- b.调用iterator()方法得到Iterator, 使用hasNext()和next()方法
- c.增强for循环, 只要可以使用Iterator的类都可以用
- d.Vector集合可以使用Enumeration的hasMoreElements()和nextElement()方法
- 2.Set
- a.调用iterator()方法得到Iterator, 使用hasNext()和next()方法
- b.增强for循环, 只要可以使用Iterator的类都可以用
- 3.普通for循环,迭代器,增强for循环是否可以在遍历的过程中删除
Map集合概述和特点
- A :Map接口和Collection接口的不同
1 Map是双列的,Collection是单列的
2 Map的键唯一,Collection的子体系Set是唯一的
3 Map集合的数据结构值针对键有效,跟值无关;Collection集合的数据结构是针对元素有效
其中HashSet跟HashMap都是用同一套Hash算法,只是HashSet把value隐藏起来了
Map集合的功能概述
A:Map集合的功能概述
a:长度功能
int size():返回集合中的键值对的个数
b:添加功能
V put(K key,V value):添加元素。
如果键是第一次存储,就直接存储元素,返回null
如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
c:删除功能
void clear():移除所有的键值对元素
V remove(Object key):根据键删除键值对元素,并把值返回
d:判断功能
boolean containsKey(Object key):判断集合是否包含指定的键
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty():判断集合是否为空
d:获取功能
*
V get(Object key):根据键获取值
* Set keySet():获取集合中所有键的集合
* Collection values():获取集合中所有值的集合
* Set<Map.Entry<K,V>> entrySet():
Map集合没有迭代器Iterator的方法(API文档),但可以使用KeySet来获得所有的key集合Set,Set用迭代器,再调用get()方法就可以获得对应的value了
Map集合的遍历之键找值
A:键找值思路:
获取所有键的集合
遍历键的集合,获取到每一个键
根据键找值
Map<String, Integer> map = new HashMap<>(); map.put("张三", 23); map.put("李四", 24); map.put("王五", 25); map.put("赵六", 26);
// Integer i = map.get(“张三”); //根据键获取值 //
System.out.println(i);
//获取所有的键
/*Set<String> keySet = map.keySet(); //获取所有键的集合
Iterator<String> it = keySet.iterator(); //获取迭代器
while(it.hasNext()) { //判断集合中是否有元素
String key = it.next(); //获取每一个键
Integer value = map.get(key); //根据键获取值
System.out.println(key + "=" + value);
}*/
//使用增强for循环遍历
for(String key : map.keySet()) { //map.keySet()是所有键的集合
System.out.println(key + "=" + map.get(key));
}
}
Map集合的遍历之键值对对象找键和值
A:键值对对象找键和值思路:
获取所有键值对对象的集合
遍历键值对对象的集合,获取到每一个键值对对象
根据键值对对象找键和值
Map<String, Integer> map = new HashMap<>();
map.put(“张三”, 23);
map.put(“李四”, 24);
map.put(“王五”, 25);
map.put(“赵六”, 26);
//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对象
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(Entry<String, Integer> en : map.entrySet()) {
System.out.println(en.getKey() + "=" + en.getValue());
}
HashMap和Hashtable的区别
A:面试题
- HashMap和Hashtable的区别
Hashtable是JDK1.0版本出现的,是线程安全的,效率低,HashMap是JDK1.2版本出现的,是线程不安全的,效率高
Hashtable不可以存储null键和null值,HashMap可以存储null键和null值
- Hashtable中的同步指的是什么意思?
同步意味着在一个时间点只能有一个时间线程可以修改hash表,任何线程在执行Hashtable的更新操作前都需要获取对象锁,其他线程则需要等待锁的释放
- 如何实现HashMap的同步?
HashMap可以通过 Map map=Collections.synchronizedMap(new HashMap())来到达同步的效果.具体而言,该方法返一个同步的map,该map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的
// 创建集合对象 LinkedHashMap<String, String> hm = new LinkedHashMap<String, String>(); //同步 Map<String, String> map = Collections.synchronizedMap(hm); // 创建并添加元素 map.put("2345", "hello"); map.put("1234", "world"); map.put("3456", "java"); map.put("1234", "javaee"); map.put("3456", "android"); // 遍历 Set<String> set = hm.keySet(); for (String key : set) { String value = hm.get(key); System.out.println(key + "---" + value); }
Collections工具类的概述和常见方法讲解
A:Collections类概述
针对集合操作 的工具类
B:Collections成员方法
//下面这些方法里面,都需要泛型实现了comparable接口(String,Integer等都实现了)
public static void sort(List list) //排序
public static int binarySearch(List<?> list,T key)//二分查找法
public static T max(Collection<?> coll)//根据默认排序结果获取集合中的最大值
public static void reverse(List<?> list)//集合反转
public static void shuffle(List<?> list)//随机置换,可以用来洗牌
综合小案例
A:案例演示
模拟斗地主洗牌和发牌,牌没有排序
/**
* * A:案例演示 模拟斗地主洗牌和发牌,牌没有排序
*
* 分析: 1,买一副扑克,其实就是自己创建一个集合对象,将扑克牌存储进去 2,洗牌 3,发牌 4,看牌
*/// 1,买一副扑克,其实就是自己创建一个集合对象,将扑克牌存储进去 String[] num = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }; String[] color = { "红桃", "黑桃", "方片", "梅花" }; ArrayList<String> poker = new ArrayList<>(); // 拼接花色和数字 for (String s1 : color) { for (String s2 : num) { poker.add(s1.concat(s2)); // concat连接两个字符串 } } poker.add("小王"); poker.add("大王"); // 2,洗牌 Collections.shuffle(poker); // 3,发牌 ArrayList<String> gaojin = new ArrayList<>(); ArrayList<String> longwu = new ArrayList<>(); ArrayList<String> me = new ArrayList<>(); ArrayList<String> dipai = new ArrayList<>(); for (int i = 0; i < poker.size(); i++) { if (i >= poker.size() - 3) { dipai.add(poker.get(i)); // 将三张底牌存储在底牌集合中 } else if (i % 3 == 0) { gaojin.add(poker.get(i)); } else if (i % 3 == 1) { longwu.add(poker.get(i)); } else { me.add(poker.get(i)); } } // 4,看牌 System.out.println(gaojin); System.out.println(longwu); System.out.println(me); System.out.println(dipai);
B:案例演示
模拟斗地主洗牌和发牌并对牌进行排序的代码实现
/**
* * A:案例演示
* 模拟斗地主洗牌和发牌并对牌进行排序的代码实现
*
* 分析:
* 1,买一副扑克,其实就是自己创建一个集合对象,将扑克牌存储进去
* 2,洗牌
* 3,发牌
* 4,看牌
*/
public static void main(String[] args) {
//1,买一副扑克,其实就是自己创建一个集合对象,将扑克牌存储进去
String[] num = {“3”,“4”,“5”,“6”,“7”,“8”,“9”,“10”,“J”,“Q”,“K”,“A”,“2”};
String[] color = {“红桃”,“黑桃”,“方片”,“梅花”};
HashMap<Integer, String> hm = new HashMap<>(); //存储索引和扑克牌
ArrayList list = new ArrayList<>(); //存储索引
int index = 0;
//拼接扑克牌并索引和扑克牌存储在hm中
for(String s1 : num) { //获取数字
for(String s2 : color) { //获取颜色
hm.put(index, s2.concat(s1));
list.add(index); //将索引0到51添加到list集合中
index++;
}
}
//将小王添加到双列集合中
hm.put(index, "小王");
list.add(index); //将52索引添加到集合中
index++;
hm.put(index, "大王");
list.add(index); //将52索引添加到集合中
//2,洗牌
Collections.shuffle(list);
//3,发牌
TreeSet<Integer> gaojin = new TreeSet<>();
TreeSet<Integer> longwu = new TreeSet<>();
TreeSet<Integer> me = new TreeSet<>();
TreeSet<Integer> dipai = new TreeSet<>();
for(int i = 0; i < list.size(); i++) {
if(i >= list.size() - 3) {
dipai.add(list.get(i)); //将三张底牌存储在底牌集合中
}else if(i % 3 == 0) {
gaojin.add(list.get(i));
}else if(i % 3 == 1) {
longwu.add(list.get(i));
}else {
me.add(list.get(i));
}
}
//看牌
lookPoker(hm, gaojin, "高进");
lookPoker(hm, longwu, "龙五");
lookPoker(hm, me, "冯佳");
lookPoker(hm, dipai, "底牌");
}
/*
* 看牌
* 1,返回值类型void
* 2,参数列表HashMap,TreeSet,String name
*/
public static void lookPoker(HashMap<Integer, String> hm,TreeSet<Integer> ts ,String name) {
System.out.print(name + "的牌是:");
for(Integer i : ts) { //i代表双列集合中的每一个键
System.out.print(hm.get(i) + " ");
}
System.out.println();
}
泛型固定边界
其实都是父类引用子类对象(多态)
- A 泛型固定下边界
- ? super E Collection中出现过addAll(Collection<? extends E> c)
- B 泛型固定上边界
- ? extends E TreeSet构造方法之一TreeSet(Collection<? extends E> c)
举列:
1.Student类
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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 + "]";
}
}
2.BaseStudent继承Student
public class BaseStudent extends Student {
public BaseStudent() {
}
public BaseStudent(String name, int age) {
super(name, age);
}
}
3.1 泛型固定下边界
ArrayList<Student> list1 = new ArrayList<>();
list1.add(new Student("张三", 23));
list1.add(new Student("李四", 24));
ArrayList<BaseStudent> list2 = new ArrayList<>();
list2.add(new BaseStudent("王五", 25));
list2.add(new BaseStudent("赵六", 26));
list1.addAll(list2);
3.2 泛型固定上边界
TreeSet ts1 = new TreeSet<>(new CompareByAge());
ts1.add(new Student(“张三”, 33));
ts1.add(new Student(“李四”, 13));
ts1.add(new Student(“王五”, 23));
ts1.add(new Student(“赵六”, 43));
TreeSet<BaseStudent> ts2 = new TreeSet<>(new CompareByAge());
ts2.add(new BaseStudent("张三", 33));
ts2.add(new BaseStudent("李四", 13));
ts2.add(new BaseStudent("王五", 23));
ts2.add(new BaseStudent("赵六", 43));
System.out.println(ts2);
CompareByAge 类代码:
class CompareByAge implements Comparator {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
return num == 0 ? s1.getName().compareTo(s2.getName()) : num;
}
}
个人心得:泛型固定边界:说白了就是利用多态
集合总结:
/** * Collection Collection集合的数据结构是针对元素有效 * List(存取有序,有索引,可以重复) * ArrayList * 底层是数组实现的,线程不安全,查找和修改快,增和删比较慢 * LinkedList * 底层是链表实现的,线程不安全,增和删比较快,查找和修改比较慢 * Vector * 底层是数组实现的,线程安全的,无论增删改查都慢 * 如果查找和修改多,用ArrayList * 如果增和删多,用LinkedList * 如果都多,用ArrayList * Set(存取无序,无索引,不可以重复) * HashSet * 底层是哈希算法实现 * LinkedHashSet * 底层是链表实现,但是也是可以保证元素唯一,和HashSet原理一样 * TreeSet * 底层是二叉树算法实现 * 一般在开发的时候不需要对存储的元素排序,所以在开发的时候大多用HashSet,HashSet的效率比较高 * TreeSet在面试的时候比较多,问你有几种排序方式,和几种排序方式的区别 * Map Map集合的数据结构值针对键有效,跟值无关 * HashMap * 底层是哈希算法,针对键 * LinkedHashMap * 底层是链表,针对键 * TreeMap * 底层是二叉树算法,针对键 * 开发中用HashMap比较多 */