Scanner类
Scanner sc = new Scanner(System.in); //需要传入调用的系统输入
int firstNumber = sc.nextInt(); //获取输入的整数
String firstStr = sc.nextLine(); //获取输入的字符串,可以读取空格,Enter结束读取
String secondStr = sc.next(); //获取输入的字符串,忽略前边的空格,tab,Enter,Enter结束读取
double firstDouble = sc.nextDouble(); //获取输入的double值
boolean sc.hasNext(); //判断是否还有下一个输入
String类
- 其他知识
- java.lang包:核心类,不需要导包(自动导入)
- 正则转义方式 String s = "\\."; 第一个反斜线将第二个反斜线转义为反斜线,然后反斜线转义.
- ascii(American Standard Code for Information Interchange)
- GB2312(6千多汉字) GBK(2万多汉字) GB18030(7万多汉字)
- 基本数值比较用大于小于的运算符,对象比较都用方法(对象才知道如何比较)
- 当需要使用循环中的某个结果,可以用一个变量引用来接收。
- curd(create update read delete)
- JDK的升级:1.简化书写(通常有局限性) 2.提高效率(可能有弊端) 3.增加安全性(可能书写会麻烦)
- 数据多了,用容器来装。
- 魔法值:未经定义的常量
while((index = s.indexOf(subString, index)) != -1){
index += i;
}
- 特点
- String str = "asdf"; str也可以叫做类类型
- 字符串对象创建先看字符串常量池有没有该对象,有将该对象地址指向该引用,没有就创建对象到字符串常量池。该对象可以共享
- 字符串不能改变,所以所用的方法都会返回新的新的字符串或其他结果
- 创建方式
- String str = "asdf";在字符串常量池创建的字符串对象。只在常量池创建一个对象
- String str1 = new String("asdf"); 在堆内存创建的String对象,传进了字符串对象。在堆创建了两个对象
- 常见方法
- 构造器
- 构造空的字符串
- String s = new String(); 等效于String s = "";空字符串也是对象
- 将字节数组查ascii表变成字符串,可以有offset和length的重载(转变一部分)
- byte[] arr = {65, 66}; String s = new String(arr);
- 将字符数组变成字符串,可以有offset和count的重载(转变一部分)
- char[] arr = {'w','b'}; String s = new String(arr);
- 将int数组查unicode表变成字符串,只有offset和count一种方法
- int[] arr = {200, 201}; String s = new String(arr, 0, 2);
- 构造空的字符串
- 普通方法
- 获取
- 构造器
length() 获取字符串长度(数组的长度是属性,字符串的长度是方法)
charAt() 根据某位置获取某字符
indexOf(int) 根据某字符或数字对应的表的字符获取该字符首次出现的位置
indexOf(int,int) 从指定位置进行ch索引。
indexOf(String) 查看字符串中某子串第一次出现位置
indexOf(String,int) 从指定位置查看字符串中某子串第一次出现位置
lastIndexOf(int) 查找某字符或数字对应表字符最后一次出现位置
lastIndexOf(int,int) 从字符串某位置开始查找某字符或数字对应表字最后第一次出现位置
lastIndexOf(String) 从字符串末尾查找某子串最后一次出现位置
indexOf(String,int) 从字符串某位置查找某子串最后一次出现位置
substring(int) 从某位置获取子串到末尾
substring(int,int) 从某位置到某位置前一个字串获取
-
-
- 转换
-
split(String reg) 按某规则分割字符串
toCharArray() 将字符串打散为字符数组
getByte() 将字符串转为字节数组,中文编码两个字节,最高位是1,所以输出是负数。默认使用当前文件编码将字符串转字节数组
toUpperCase() 将字符串中的英文字符转为大写
toLowerCase() 将字符串中的英文字符转为小写
replace(char,char) 将字符串中某字符替换为另一个字符,没有找到某字符则不替换,返回原串,不创建新串
replace(CharSequence,CharSequence) 将字符串中某字符串替换为其他字符串。String,StringBuffer实现了CharSequence接口
trim() 去掉字符串两端空格
concat(String) 将字符串进行连接,更专业。指挥对象做事情
valueOf() 将基本数据类型转为字符串,类似""+ 1 + true;
-
-
- 判断
-
equals(Object) 比较字符串的内容是否相同,重写了Object的equals(),所以参数列表也一样
equalsIgnoreCase(String)比较字符串是否相同,忽略大小写
contains(CharSequence) 查看某字符串是否包含另一字符串
startsWith() 字符串是否以指定字符串开头
endsWith() 字符串是否以指定字符串结尾
-
-
- 比较
-
int compareTo(String) 按ascii表比较两个字符串。相等,返回0,此字符串小于参数字符串,返回负数(两个字符串中第一个不相同的字符差距),大于,返回正数(两个字符串中第一个不相同的字符差距)。实现了Comparable接口,重写了compareTo方法。
-
-
- 其他
-
intern() 一个初始为空的字符串池。调用该方法如果池中有该字符串,则返回字符串,没有则将字符串添加到池中,返回此对象的引用
String s1 = "asdf"; String s2 = s2.intern();//这句话等同于String s2 = "asdf";
s1创建到堆中,调用intern(),池中没有该字符串对象,在池中创建该对象并返回引用
StringBuffer类
- 含义
- 字符串缓冲区,存储数据的容器
- 特点
- 长度可变(比数组好)
- 可以存不同数据类型(数组只能存一种)
- 最后转成字符串进行使用
- 可以对字符串修改(普通字符串创建之后无法修改)
- 可变长度数组
- 满了之后新开辟一个扩充大小后的空间,将数组复制到新的内存区域,
- 常用方法
- 构造方法
new StringBuffer() 初始化后,默认能存放16个字符。
new StringBuffer("asdf") 在字符串缓冲区同时初始化字符串
new StringBuffer(int) 创建指定容量的字符串缓冲区,用于知道数据量的情况下,可以提高效率,不必新开辟空间
-
- 普通方法
- 添加
- 普通方法
StringBuffer append() 可以添加任意基本类型数据和String(除了byte和short)。char可以添加数组。默认添加到对象缓冲区中。
sb.append(1).append("true");/*方法调用链,相当于把所有数据连接起来,成了一大坨字符串。
StringBuffer insert(index,data) 将数据插入到字符串某字符下标处
-
-
- 删除
-
StringBuffer delete(start,end) 删除范围内字符串,可以清空字符串
StringBuffer deleteCharAt(index) 删除字符串缓冲区某下标字符
-
-
- 查找
-
indexOf(String) 获取子串第一次出现下标
lastIndexOf(String) 获取子串最后一次出现下标
charAt(int) 获取某下标字符
-
-
- 修改
-
StringBuffer replace(start,end,string) 替换字符串缓冲区start到end-1的字符串
void setCharAt(int,char) 修改某下标字符
-
-
- 其他
-
length() 获取字符串缓冲区长度
setLength(num) 设置缓冲区长度(多的裁掉,少的补上空格),也可以起到清空缓冲区的功能
StringBuffer reverse() 反转字符串
StringBulider类
- 特点
- 兼容StringBuffer,功能和用法一样。
- StringBuffer保证线程安全(线程同步),StringBulider不保证线程安全
- 不执行同步,速度更快(不需要判断锁)
- 解析
- StringBuffer有append()和delete(),不同线程同时操作可能不同步。
- 但是在单线程中不需要判断锁,所以使用StringBulider,提高缓冲区效率
- StringBuffer和StringBuilder用途
- 存的数据最后用字符串来进行显示或操作,就用这两个方式(如果用String,会在字符串常量池产生很多字符串常量)
- 示例
public static String arrayToString(int[] arr){
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if(i != arr.length - 1)
sb.append(arr[i] + ",");/*如果用String,每连接一次逗号,字符串常量池就多一个字符串常量,有很多冗余数据*/
else
sb.append(arr[i] + "]");
}
return sb.toString();/*将StringBuffer类型转为String*/
}
基本数据类型对象包装类
- 小知识
- wrapper 包装
- parse 转换
- refactor 重构
- separator 分隔
- 声明异常就是希望被做处理
- 代码中常用的数据可以做成常量
- 将自己的代码封装为方法,项目中更好识别。是一种代码转换动作。
- 特点
- 为了更方便操作基本数据类型值,所以对其进行了对象封装。在对象中定义了属性和行为,丰富了其操作
- 用于描述该对象的类就叫做基本数据类型对象包装类
- 相关包装类
- Byte Short Integer Long Float Double Character Boolean
- 作用
- 主要用于基本数据类型和字符串的转换
- 基本类型-->字符串
- “” + 基本类型
- 用String类的valueOf(值)
- 用包装类的valueOf(值)返回包装类,需要继续使用toString()
- 字符串-->基本类型
- 使用包装类的静态方法:xxx parseXxx("xxx"),Character没有,可以直接读取字符串的某个字符
- 字符串被包装类进行了封装(初始化的时候填入),使用xxxValue()将包装类对象转成基本数据类型
Integer i = new Integer("123"); System.out.println(i.intValue() + 1); //124
-
Integer类
- 常用属性
Integer.MAX_VALUE 获取int最大值
Integer.MIN_VALUE 获取int最大值
-
- 常用方法
- 构造方法
- 常用方法
new Integer(num); 可以放一个int值
new Integer(String); 可以放一个String的int值
Integer i = 4; 简写(自动装箱),相当于new Integer(4)。因为是引用类型,赋值null,自动拆箱会抛异常。需要相应处理
i = i + 6; 这里i是包装类,相当于进行了i.intValue()(自动拆箱)
show(55);/*调用时相当于Object a = new Integer(55);自动装箱*/
public static void show(Object a){
System.out.println(a);
}
-
-
- 普通方法
- 十进制转其他进制
- 普通方法
-
Integer.toBinaryString(int); 将十进制转成二进制
Integer.toHexString(int); 将十进制转为十六进制
Integer.toOctalString(int); 将十进制转为八进制
Integer.toString(num,进制); 将num转为某进制,不是Object的toString()的重写
-
-
-
- 其他进制转十进制
-
-
Integer.parseInt(String,进制); 将某进制的字符串转为十进制整数
-
-
-
- 字符串转整形
-
-
Integer.parseInt(String); 将整数字符串数字转为int型,有异常抛出,可以判断是否输入了数字。
-
-
-
- 其他
-
-
equals() 比较包装类中的值是否相同
compareTo() 比较包装类中的值,返回 0 相等 1 前大 2 后大
Integer a = new Integer("3");
Integer b = new Integer(3);
System.out.println(a.equals(b));/*比较的时候会先将字符串3转为数字3*/
System.out.println(a.compareTo(b));//结果为0
集合类
- 位置
- utility 公用程序
- 在java.util包中,工具包
- 特点
- 是一个容器,用于存很多的对象
- 长度可变
- 不可以存基本数据类型
- 可以存不同类型的数据
- 容器自身都有特定的结构(类比有茶格和没茶格的杯子),叫做数据结构(数据存放方式)。依据不同需求用不同容器
- 集合容器因为内部的数据结构不同,有多种容器
- 小知识
- 将数据一点一点向上抽取
- 体系的学习是看顶层结构,用底层功能
- bean类,Java的类的一种表现形式包括于描述事物的属性,set,get方法,以及一个空参构造函数
- 集合使用小技巧
- 元素需要唯一吗?
- 唯一:Set
- 需要指定顺序吗
- 需要:TreeSet,
- 不需要:HashSet
- 需要和存储一致的顺序(有序):LinkedHashSet
- 需要指定顺序吗
- 不唯一:List
- 需要频繁增删吗
- 需要:LinkedList
- 不需要:ArrayList
- 同步:Vector
- 需要频繁增删吗
- 唯一:Set
- 如何记住每一个容器的接口和所属体系?
- 看后缀名字,后缀名就是List或者Set体系
- 前缀名是该集合的数据结构
- array是数组(有角标,查询快)
- linked是链表(增删快,要想到add,get,remove,first,last等方法)
- hash是哈希表(唯一性,元素需要覆盖hashCode和equals方法)
- tree是二叉树(要想到排序,Comparable和Comparator接口)
- 常用集合容器都是不同步的
- 元素需要唯一吗?
- 集合类关系图
- 虚线都是接口,实线的都是实现类
- List中常用的有ArrayList LinkedList Vector
- 集合体系
- Collection(集合根接口)
- 常见方法(所有集合体系都有下列方法)
- 插入
- 常见方法(所有集合体系都有下列方法)
- Collection(集合根接口)
boolean add(E) 给集合添加对象,返回添加是否成功
boolean addAll(Collection coll) 给集合添加很多对象,返回是否添加成功。会追加所有的元素,可能有相同的。这里没有限制添加的都是什么类型的元素,有类型安全隐患
boolean addAll(Collection<? extends E> coll) 这里限制了Collection对象添加的元素都是其泛型E或其子类,所以才能统一操作
-
-
-
- 删除
-
-
boolean remove(obj) 给集合删除对象,返回是否删除成功。可以用于改变集合的长度
boolean removeAll(Collection<?> coll) 给集合删除很多对象,返回是否删除成功。删除调用者的交集。底层使用equals,因为所有对象都有该方法,所以可以传任意对象,因此使用?通配符
void clear() 清空集合中的所有对象
-
-
-
- 判断
-
-
boolean contains(Obj) 查看集合是否包含某对象
boolean containsAll(Collection<?> coll)查看集合是否包含某组所有对象.用通配符是因为底层实现用equals,每个对象都具备,所以所有对象都支持比较。也就可以任意传对象了。
boolean isEmpty() 判断集合是否有元素(用size()是否为0判断的)
boolean equals() 判断两个集合是否相等
-
-
-
- 获取
-
-
int size() 获取集合中对象个数
Iterator iterator() 返回迭代器对象,使用迭代器获取元素。Iterator是接口 Iterator it = coll.iterator();
该对象依赖于具体的容器(不同容器数据结构不同),因此该对象需要在容器内部实现。使用者无需了解内部实现,通过容器
获取实现的迭代器对象即可。用Iterator接口统一取出。
-
-
-
- 其他
-
-
boolean retainAll(Collection coll) 取两个集合的交集覆盖到调用集合
Object[] toArray() 将集合转成数组,返回了Object类型的数组。
<T> T toArray(T[] a) 将集合转成数组,可以对集合中元素操作的方法进行限定,使其不允许增删。可以用,不允许动里边的数据。Set也可以转。
需要传入一个指定类型的数组。最好长度指定为集合的size
如果长度小于集合的size,该方法创建一个同类型并和集合相同size的数组
如果长度大于集合的size,该方法将会使用指定的数组长度存储集合元素,其他位置默认为null
-
-
- 常用操作
- 直接输出集合对象,可以输出所有集合中的元素System.out.println(coll);
- 常用操作
- List(列表集合,Collection子接口,可以使用Collection所有方法)
- 特点
- 有序(存入和取出的顺序一致)
- 元素都有索引
- 元素可以重复
- 可以对元素进行增删改查。
- 特有常见方法(除Collection中的方法)
- 添加
- 特点
-
void add(index,elem) 添加元素到某下标
boolean addAll(index, collection) 添加集合到某下标
-
-
-
- 删除
-
-
Object remove(index) 删除某下标元素并返回该元素
-
-
-
- 修改
-
-
Object set(index, Element) 将某下标修改为替换元素,返回旧元素
-
-
-
- 获取
-
-
Object get(index) 获取某下标元素
int indexOf(Object) 索引某对象位置
int lastIndexOf(Object) 索引某对象最后一次出现的位置
List subList(from,to) 获取子列表
/*list的特有遍历方式,set不行,可以用for循环获得,也可以用while获得*/
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
ListIterator iterator() 获取列表迭代器
ListIterator iterator(index) 从list某下标开始获取列表迭代器
-
-
- 可能出现的异常
-
ConcurrentModificationException 集合和迭代器同时在对同一个元素进行操作,迭代出现了问题。
1.可以操作集合的时候不迭代,操作迭代的时候不集合
2.使用List的listIterator()拿到列表迭代器进行集合操作
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
Iterator it = list.iterator();
while(it.hasNext()){
Object o = it.next();
if(o.equals("abc2"))
list.add("abc9");/*迭代过程中,操作集合时候可能出现ConcurrentModificationException异常,可以使用Iterator子接口ListIterator来完成迭代中对集合更多的操作。*/
else
System.out.println("next:" + o);
}
System.out.println(list);
-
- 实现List的常用子类
- 集合中除了Vector,都是非同步的(效率更高)。
- 集合当中更多存的是自定义对象。
- Vector(1.0出现,几乎不再用)
- 特点
- 内部是数组数据结构
- 是线程安全的(同步)
- 数组大小可变(增加一倍,浪费空间,效率更低)
- 增删查询都慢。
- 常用方法
- 特点
- 实现List的常用子类
Enumeration elements() 返回向量中的枚举,Vector特有的遍历方法,功能与Iterator相同,但因名字过长被Iterator代替
(也可以使用下标或者Iterator来遍历)
while(e.hasMoreElements()){
System.out.println(e.nextElement());
}
-
-
- ArrayList
- 特点
- 内部是数组数据结构
- 是线程不安全的(不同步),替代了Vector,单线程效率更高(多线程仍使用ArrayList,要么加锁,要么用其他方式)
- 数组大小可变(增加一半)
- 增删元素速度慢(需要进行数据的大量移动),查询速度快(下标直接读取)
- 默认创建的列表长度为10
- 如果添加数字,可以直接使用a1.add(5);这里相当于对数字5进行了自动装箱,省去了new Integer(5);的步骤。本质还是需要添加对象。
- 基本数据类型赋值给引用数据类型,才会进行装箱。例如
- public void show1(Integer num){}
- public void show2(int num){}
- show1(6),赋值给了引用数据类型,进行了装箱。相当于Integer num = new Integer(6);
- show2(6),赋值给了基本数据类型,没有进行装箱。相当于int num = 6;
- 引用数据类型和基本数据类型进行运算的时候就会自动拆箱
- public void show1(Integer num){num +8;}
- 图解
- 小例子
- 创建一个ArrayList,会默认有10个空间。每个空间填充一个对象的引用(即创建的对象地址),真实的对象并不放到ArrayList中。所以可以存放不同类型的对象。
- 迭代ArrayList中的对象的时候,使用的就是里边装的引用。
- 特点
- LinkedList
- 特点
- 内部是链接数据结构(用指针域记录数据的地址)
- 是线程不安全的(不同步)
- 增删元素速度快(使用指针指向),查询速度慢(需要用指针一个一个遍历)
- 有索引,因为是List的子类,但找的时候是一个一个往下找的
- 图解
- 特有常见方法
- 添加
- 特点
- ArrayList
-
void addFirst(E) 将元素添加到链表开头
void addLast(E) 将元素添加到链接结尾
boolean offerFirst(E) 将元素添加到链表开头
boolean offerLast(E) 将元素添加到链接结尾
void push(E) 插入到第一个元素
-
-
-
-
- 获取
-
-
-
E getFirst() 获取链表第一个元素,指针不移动。如果没有元素会抛异常NoSuchElementException
E getLast() 获取链表最后一个元素,指针不移动。如果没有元素会抛异常NoSuchElementException
E peekFirst() 获取链表第一个元素,指针不移动。如果没有元素会返回null
E peekLast() 获取链表最后一个元素,指针不移动。如果没有元素会返回null
E peek() 获取链表第一个元素,但不将第一个元素删除。
-
-
-
-
- 删除
-
-
-
E removeFirst() 移除并返回链表第一个元素(可以用于LinkedList的正向遍历,但是所有元素会被删掉)。如果没有元素会抛异常NoSuchElementException
E removeLast() 移除并返回链表最后一个元素(可以用于LinkedList的反向遍历,但是所有元素会被删掉)。如果没有元素会抛异常NoSuchElementException
while(!link.isEmpty()){
System.out.println(link.removeFirst());
}
E pop() 第一个元素弹出,并将当前的第一个元素删除。
E poll() 获取第一个元素并删除该元素。与pop()效果相同
E pollFirst() 移除并返回链表第一个元素(可以用于LinkedList的正向遍历,但是所有元素会被删掉)。如果没有元素会返回null
E pollLast() 移除并返回链表最后一个元素(可以用于LinkedList的反向遍历,但是所有元素会被删掉)。如果没有元素会返回null
-
- Set(列表集合,Collection子接口)
- 特点
- 无序(存和取的顺序可能不同)
- 元素不重复
- 其中的方法与Collection一致
- Set集合的取出方式只有迭代器。
- 特点
- 实现Set的常用子类
- HashSet(使用HashMap来)
- 特点
- 内部数据结构是哈希表(不保证迭代顺序,不保证顺序恒久不变)
- 存储方式由算法完成。算法改变,存储顺序改变。
- 可以存null
- 此实现是非同步的
- 小知识
- 哈希表:通过哈希算法算出来的哈希值的集合。
- 哈希表内部仍旧是数组,但根据元素特征算出了存放位置,访问更快。(查找元素的位置的时候就不需要遍历数组,根据元素计算之后得到的索引就可以得到该元素)
- 每个对象都从Object继承来了哈希值,方便找某个元素的位置,而不必去遍历找某个元素在哪里。默认的hashCode()方法由系统底层实现。自己重写该方法,可以建立自己的哈希值。
- 元素都根据自身特点算出位置,所以相同元素,位置相同,因而不会有重复元素。
- 哈希算法最后会进行取模运算,以达到将大的计算结果存到较小长度的数组里。
- 哈希表判断两个元素是否相同
- 判断两个元素哈希值是否相同。相同则判断两对象内容是否相同
- 判断哈希值相同,判断的就是对象的hashCode方法。判断对象内容是否相同,用equals方法
- 哈希值不同,则不需要判断equals
- 哈希冲突:哈希值一样,但是内容不一样,这个时候没有办法存入进去。但哈希算法比较复杂,一般没有该情况。
- 出现冲突后:
- 1 可以将该元素顺延存放。(位置不够加长数组)。
- 2 串联:根据该哈希算法算出来的位置在算一个位置。在原位置在挂一个。
- 出现冲突后:
- 使用HashSet存储自定义对象,可以重写hashCode()方法来计算存储的位置,同时重写equals()来判断两个对象是否相同。
- 在存储Person对象的时候,hashCode返回int,所以可以返回name的hashCode值+age(name是String类型,拥有int类型的hashCode值)
- HashSet在使用add()的时候会自动调用这两个方法来进行判断。先调用hashCode,如果插入位置没有元素,则不调用equals方法。否则调用equals方法。
- 为了尽量保证hash值唯一,可以给数字乘一些数,然后拉大不同运算参数之间的距离。
- 具体对于hashCode或者equals需要重写哪一个,可以查看文档进行查询。
- 健壮性:在各种出错条件下恢复能力的一种测度。
- ArrayList和HashSet的区别:
- ArrayList判断元素只需要使用equals方法即可(删除的时候只判断equals)
- HashSet判断元素需要使用hashCode和equals方法(删除的时候判断hashCode与equals,所以重写hashCode也是为了可以删除)
- 判断方式的不同是因为使用了不同的数据结构
- 特点
- LinkedHashSet
- 特点
- 集合中的数据唯一,且有序
- HashSet子类
- 哈希表和链表实现(通过指针域来存储下一个添加元素的地址)
- 用法
- HashSet hs = new LinkedHashSet();//用多态的方式来改变存储过程。这样就可以以链表形式存储。
- Set与List异同
- Set元素唯一,List元素不唯一
- 都可以使用有序存储,Set使用LinkedHashSet就可以有序存储。使用HashSet是无序存储。
- 特点
- TreeSet
- 特点
- 存入元素按照元素的自然顺序排列(字符串是按照字典顺序进行排序的)
- 需要根据元素的比较结果进行排序。TreeSet自身不具备比较功能。
- 实现不同步
- TreeSet和HashSet的底层代码是由TreeMap和HashMap实现。为了保证单列集合中元素的唯一性,所以通过Map创建了Set,更方便应用。(相当于只用了HashMap中的键来保证Set的唯一性)
- TreeSet对元素排序的方法
- 1 自然排序,使用某类默认的比较方式(compareTo),使元素自身拥有比较功能
- 存入对象实现Comparable接口,重写compareTo方法。(普通对象默认没有排序方法)。不重写会报转换异常错误。(需要使用Comparable接口来使用该对象重写的compareTo方法进行比较)
- 2 使集合自身拥有比较功能(存入的对象没有比较功能,或比较功能不是自己所需要的方式)。需要在添加元素之前,创建对象时进行功能添加
- 使用传入实现了Comparator接口的对象的构造函数。
- 1 自然排序,使用某类默认的比较方式(compareTo),使元素自身拥有比较功能
- 与hashCode()和equals()方法无关,使用compareTo方法来进行元素是否相同的判断。
- 小知识
- 块注释可以添加到代码之间 public static /*hello*/ void show(){}
- 从小到大和从大到小输出的转换
- 交换返回值(不合适,因为代码还需要改)
- 交换比较的对象(合适,只需要在使用端进行改变即可,对象引用的操作更方便)
- 三元运算符的使用:(注意,字符串本身就实现了Comparable,所以可以直接进行比较)
- 后边的代码更加简洁,而且可以实现相同的功能。
- 在进行比较的时候,通常依据的条件不唯一。有主要条件的同时可能会有次要条件,主要条件相同,就按照次要条件比较。需要更加详细的分析。
- 如果对象之间需要比较,就统一实现Comparable接口。然后比较工作直接让Comparable接口进行接管。
- 自然排序实现过程
- 比较的对象需要implements Comparable,并重写compareTo方法
- compareTo的重写按照实际情况来决定使用哪个数据来进行判断。
- Set集合元素不重复,判断的时候使用compareTo方法的返回值。如果返回值是0,则代表两个元素相同,也就不再插入。所以可以通过返回值的变化来进行元素是否相同的判断。
- 使用集合自身排序的过程(使用Comparator比较器,比较器比自然排序更常用)
- 创建一个类implements Comparator, 重写compare方法,
- 创建TreeSet的同时传入该实现类的实例。
- 重写之后会自动使用compare方法来进行比较,而不使用元素自身比较方式比较。
- Comparator有两个抽象方法,不需要重写equals是因为继承了Object已经进行了重写。有这个equals方法主要是为了判断比较器是否相同
- 底层结构
- 使用二叉树插入方式,排序后插入,对于排序而言效率很高。相同数据则不再插入。
- 二叉树每一个结点存三个引用。父引用,左右子结点引用
- 二叉树提高效率:可以先对已有元素折半,然后再添加元素。从中间开始找。
- TreeSet实现怎么存,怎么取的功能
- Comparable的compareTo方法或Comparator的compare方法返回1,每次插进来的元素都比原来的大,从小到大取,则是源输入流程
- 返回-1,每次插进来的元素都比原来的小,从小到大 取,相当于倒序输出源输入流程
- 特点
- HashSet(使用HashMap来)
- Map(使用频率相当高)
- 特点
- 一次添加一对元素(Collection一次添加一个元素),存储键值对
- Map集合叫做双列集合。Collection集合叫做单列集合。
- 每个键映射一个值,有键的唯一性
- 创建的时候使用泛型。
Map<Integer, String> map = new HashMap<Integer, String>();
通过泛型来指定传入的键值是什么类型。
- Map没有迭代器,与Collection不同。如果想要遍历所有的值,只能够通过keySet()取出所有的键,然后遍历每一个键取出值
- Map也可以这样存<"1号教室", Set<Student>> ,相当于一个键对应一个集合,这样可以放更多的值。相当于1对多。这里只需要放对象即可
- 用途
- 在有映射关系的时候,可以优先考虑
- 在查表法中用的较多
- 小知识
- 视图:可以看作可视化的映射关系。会返回某种结构。
- 事物变复杂了,可以使用封装对象,将复杂的东西赋予给一个总的头来进行操纵。例如归属地
1 只有北京,上海等一个字段,可以直接用String来进行存储
2 如果是北京的朝阳的定福庄的什么,比较复杂,需要多个字段,则可以在一个对象中对这些数据进行封装。
- 常见方法
- 添加
- 特点
- Set(列表集合,Collection子接口)
value put(key, value) 给某个键下添加值。如果原来该键下没有值,就返回null,有值则返回该值
-
-
-
- 删除
-
-
void clear() 清空Map集合
value remove(key) 根据指定的key删除这个键值对,返回该键值对中的值
-
-
-
- 判断
-
-
boolean containsKey(key) 判断Map是否包含某个键
boolean comtainsValue(value) 判断Map是否包含某个值
boolean isEmpty() 判断Map是否为空
-
-
-
- 获取
-
-
value get(key) 通过键获取值。没有该键,返回null。也可以通过返回null来判断是否包含指定键。可以直接用get结果替代containsKey
size() 获取键值对的个数
toString() 可以直接打印Map对象,用来查看Map中所有元素,形式为{8, "旺财", 9, "小强"}
Set<keyType> keySet() 返回Map中所有的键的set视图。(相当于返回丈夫)可以再继续通过Set的迭代器得到每一个键,之后获取Map中所有的值。
Set<Integer> allkeys = map.keySet();/*需要在泛型中指定该Set元素的类型,键会自动在后续过程中装箱拆箱。*/
Set<Map.Entry<k,v>> entrySet() 返回Map中的所有键值映射关系。(相当于返回结婚证书)映射关系的类型叫做Map.Entry。取出的是每一个键值对(键和值被封装到了一个对象里边)。可以通过getKey(),getValue()方法获得键和值。
Collection<valueType> values() 以Collection返回Map中所有键值对中的值。
-
- Map的常用子类
- HashTable
- 特点
- 内部结构是哈希表
- 1.0版本就有(单列集合是Vector,双列集合是HashTable)
- 同步的
- 不允许null键和null值
- 特点
- Properties
- 特点
- HashTable子类
- 用于属性集,用于存储配置文件的信息(键值对型配置文件信息)
- 可以和IO结合
- 特点
- HashMap
- 特点
- 内部结构是哈希表
- 不是同步的
- 允许null键和null值
- HashMap是无序的,对于HashMap<Student, String>,插入内容相同但是不同对象的键,就需要重写hashCode和equals.当判断两个对象的hashCode相同的时候,也就表示这两个对象的标识一样,接着判断equals是否相同。equals比较的Student内部数据是否相同。相同将不再插入该键,只是进行相应值的修改。否则插入该键值
- HashMap遍历出来的顺序按存储的键进行排序。有序是如果按照怎样存怎样取,可以使用LinkedHashMap,用HashMap接收即可。
- 哈希表的数值对象的哈希值和数值排序顺序一样。
- 特点
- TreeMap
- 特点
- 内部结构是二叉树
- 不是同步的
- 可以对Map集合中的键进行排序
- TreeMap按照二叉树排序,这样的话要么需要插入的元素实现了Comparable接口,要么需要集合传入Comparator的比较器。如果比较器用的是父类的比较器,子类进行排序,也是可以进行排序的。因为子类继承了父类的成员,所以可以使用与父类相同的比较方式。如果子类需要的比较方式和父类不同,那么就需要重新创建一个子类的比较器。
- 特点
- HashTable
- Map的常用子类
- Iterator接口
- 常用方法
- boolean hasNext() 判断是否还有下一个元素
- E next() 每次调用获取下一个元素,因为是Object类型,所以可以使用Object obj = it.next()接收。
- void remove() 在Iterator指向的Collection中删除获取的当前元素
- 使用方式
- 常用方法
/*while使用*/
while(it.hasNext()){
System.out.println(it.next());
}
/*for使用,迭代器使用完了时候会自动从内存中删除该引用。用于迭代后迭代器已经无用的情况*/
for(Iterator it = coll.iterator(); it.hasNext(); ){
System.out.println(it.next());
}
-
- 设计方式
- 因为每一种容器的数据结构不同,所以每一种容器的取出方式也不一样,所以将取出的方式放到容器中,变成内部类,该内部类会更了解该容器的取出具体的实现(取出的过程可能需要容器的某些方法或属性进行操作)。而为了让所有的数据取出都有统一的规则,所以使用向上抽取为Iterator接口来统一管理,在重写的方法中具体实现取出的过程,返回内部的数据。
- 容器和取出方式之间的耦合性降低了。
- 迭代器就是实现了Iterator接口的每一个容器的内部类对象。
- 设计方式
- ListIterator接口(Iterator子接口)
- 特点
- List专有,用于迭代过程中对集合进行更多的操作。
- 可以在迭代过程中对List进行增删改查
- 可以正序或反序遍历。
- 常用方法(ListIterator lit = list.listIterator())
- 特点
void add(E) 获取了某元素的指针,将新元素该元素后边(操作的是ListIterator对应的List)
void set(E) 将List当前元素修改为该元素
boolean hasPrevious() 判断是否有上一个元素(可以指针通过hasNext遍历到之后使用)
E previous() 获取上一个元素
int nextIndex() 返回随后调用元素在列表的索引
int previousIndex() 返回前一个调用元素在列表的索引
void remove() 删除列表中next()或previous()返回的元素。
- Enumeration接口
- 特点
- Vector特有的可以遍历成员的方法(List一般是for或迭代器)
- 常用方法
- 特点
boolean hasMoreElement() 查看枚举是否有更多元素
E nextElement() 获取下一个元素
- Map.Entry接口
- 特点
- 返回Map中所有的键值对。可以看作键值映射关系对象。
- 是Map接口的内部接口,默认会加上public static
- 常用方法
- 特点
getKey() 获取一个键值对的键
getValue() 获取一个键值对的值
setValue() 替换本键值对中的值,返回原来存的值。
- Utilities(集合框架常见工具类,Collections和Arrays,用来丰富细小功能操作)
- Collections
- 特点
- Collection是接口,Collections是集合框架的工具类。
- 都是静态方法
- 小知识
- Java中比较数组用的是大于小于,比较对象,用的是Comparable的comapreTo或者Comparator的compare方法。
- 常用方法
- 特点
- Collections
<T extends Comparable<? super T>> sort(List<T> list) 因为希望Collections的sort方法可以比较所有对象,所以用泛型来进行定义。但并不是所有的对象都实现了比较的方法,因此限定只有实现了Comparable接口的类或子类才能进行比较,但这里为了提高扩展性,又可以添加T的实现了Comparable接口的类或其父类。限定了可以传入的类的类型,就可以在后边的List<T>中进行使用了。明白了原理就可以自己写了。
<T> void sort(List<T>, Comparator<? super T> c) 如果想要自定义排序方式,可以传入比较器来进行排序。不受限于对象本身的compareTo()方法。TreeSet就利用了该特性,完成了二叉树的特性比较。
void swap(list, i, j) 用于交换列表中的元素
<T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 折半查找,如果需要进行查找的话,源数据需要先进行排序,之后根据key进行查找。返回负数表示不存在该元素(-1),可以在-(1+index)的位置插入。比下标多一个就是为了保证找不到元素的时候,每次都可以返回负数。如果插入0下标处就返回-1
<T> int binarySearch(List<? extends T>> list, T key, Comparator<? super T> c) 如果需要按照自定义顺序排列再查找,可以传入比较器后查找
<T extends Object & comparable<? super T>>T max(Collection<? extends T> coll) 对集合(list或set)按照自然顺序取出最大的元素。这里的泛型限定可以看作<T extends Object>和<T extends comparable<? super T>>
<T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 可以传入比较器进行最大值的取出。
<T> Comparable<T> Collections.reverseOrder() 返回一个比较器,将自然顺序排序的元素逆转。对象排序,想要逆转都用这个。传入这个方法,比较的对象会自动传入,然后反转比较过程即可。(o1.compareTo(o2)变成了o2.compareTo(o1))
<T> Comparable<T> Collections.reverseOrder(Comparator<T> comp) 返回一个比较器,将已有比较器排序的元素逆转。
void reverse(List<?> list) 反转List的排序
<T> boolean replaceAll(List<T> list, T oldVal, T newVal) 将List中的所有oldVal替换为newVal,可以看作set(indexOf(oldVal), newVal),将代码进行了合并操作。
<T> fill(List<? super T> list, T obj) 将所有元素都换成同一个元素。可以用于集合中所有元素初始化。
void shuffle(List<?> list) 将传入的集合随机的进行排序。相当于扑克牌洗牌
<T> ArrayList<T> list<Enumeration<T> e> 将枚举转为List
<T> Enumeration<T> enumeration<Collection<T> c> 将集合转为枚举
重点方法
集合用于多线程,不安全需要加锁。使用Collections的方法进行加锁操作。
<T> List<T> synchronizedList(List<T> list) 将非同步的list转为同步的list并返回。
<T> Collection<T> synchronizedCollection(Collection<T> c) 将非同步的Collection转为同步的Collection并返回。
<T> Map<K,V> synchronizedMap(Map<K,V> m) 将非同步的Map转为同步的Map并返回。
<T> Set<T> synchronizedSet(Set<T> s) 将非同步的Set转为同步的Set并返回。
-
- Arrays
- 特点
- 方法都是静态的
- 除了基本类型,对象,泛型都可以操作。
- 小知识
- toString的经典实现
- 特点
- Arrays
public static String toString(Object[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) { /*中间省略了条件判断,提高了效率*/
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
-
-
- 常用方法
-
boolean equals(int[] a,int[] b) 比较两个数组是否相同
void fill(int[] a, int val) 将数组所有元素替换为指定元素
void fill(int[] a, int fromIndex, int toIndex, int val) 将数组部分元素替换为指定元素
void sort(int[] a) 数组的排序
void sort(int[] a, int fromIndex, int toIndex) 数组的排序
int binarySearch(arr, 45) 二分法查找45的index值.返回正数表示下标值
返回负数表示不存在该元素(-1),可以在-(1+index)的位置插入.
boolean deepEquals(Object[] a1, Object[] a2) 比较数组里的元素对象,同时还会比较对象里边的内容
String toString(int[] a) 返回数组内容的字符串表达形式。普通数组打印出来,toString是类名+hashCode,用Arrays的toString()打印更有意义。
<T> List<T> asList(T[] t) 将数组转为List集合。因为数组功能有限,所以转为List可以进行更多操作。
例如使用List的包含操作。可以使用集合的方法操作数组。
1 但由于数组长度是固定的,所以不可以使用集合的增删方法,不可以改变数组长度。否则会有UnsupportOperationException
String[] testStr = {"asdf", "haha"};/*不使用List的contains方法,就需要写自己关于String的方法*/
List<String> list = Arrays.asList(testStr);
System.out.println(list.contains("asdf"));
//list.add("fdab");不能操作,因为数组长度固定,这里不继续添加。但是修改,判断可以使用。
2 如果数组中的元素是对象。转成集合时,直接将数组中的元素作为集合中的元素进行集合存储
如果数组中的元素是基本类型数值,会将该数组作为集合中的元素进行存储。基本数据类型无法存入集合。
int[] arr = {1, 2, 3};/*如果这里用了Integer[],存进去的是4个数据,相当于存了对象*/
List<int[]> list = Arrays.asList(arr);/*将数组对象存了进去,泛型类型和arr修饰符一样即可*/
System.out.println(list);
- 给非同步集合加锁原理
- 日常使用,直接用Collections.synchronizedList即可。
/*
将一个非同步的Collection变成同步的,需要给所有的方法都进行加锁操作。
下边只是演示代码,未重写所有方法,使用会报错。
使用一个工具类,里边使用一个静态方法synchronizedList返回给传入的List加锁后的List
创建工具类中的内部类,创建私有变量接收List,创建一个锁,之后重写所有抽象方法,加上同步锁即可。
*/
class MyCollections {
public static List synchronizedList(List list) {
return new MyList(list); /* 接收list对象,返回加锁后的list对象*/
}
private class MyList implements List { /* 只实现了list接口,可以对list的对象进行加锁操作 */
private List list;
private final Object lock = new Object();
MyList (List list) {
this.list = list;
}
public boolean add(Object obj) {
synchronized(lock) {
return list.add(obj); /*重写list方法,将传入的list对象的该方法进行同步封装。*/
}
}
public boolean remove(Object obj) {
synchronized(lock) {
return list.remove(obj);/*所有的抽象方法用原功能实现就可以,仅仅加个锁就行*/
}
}
}
}
泛型
- 特点
- 英文翻译为genetic
- JDK1.5后出现。
- 如果使用泛型的话,开发必须写。创建容器的时候就明确要放什么类型的数据。
- 避免存入了不合适的数据类型,编译时不会报错,运行时出了问题,减少后期隐患。
- 例如ArrayList,存了String和Integer两种数据,后边只将数据转换为String,报错,所以只能存的时候自己保证存的都是同一个类型(程序员主观判断)。但这样极易出错,用泛型来指定数据类型,会使代码编写更加严谨。
- 没有加泛型,导致下边取出数据的时候出错。
- 中括号用于数组,小括号用于方法,大括号用于定义范围,只能用尖括号表示泛型。
- ArrayList<E> 这里E大写,因为要传入引用数据类型(类,接口,数组),类接口的首字母都是大写的。所以这里也大写。
- 是用于编译时期的安全技术,提高了编译时期的安全性。因为编译没有问题,所以在运行时候才没有问题。
- 泛型技术是给编译器用的技术。确保了类型的安全。
- 运行的时候,会将泛型去掉,生成的class文件是不带泛型的。这个称为泛型的擦除。因为用的类加载器是以前的,为了使加入泛型后的代码,以前的类加载器仍旧可以兼容使用,所以去掉了泛型,仍旧使用旧加载器加载。
- 泛型的补偿
- 因为写代码的时候加了泛型,运行的时候去掉了泛型。所以add添加的时候仍旧是Object类型,但是在代码遍历中却不用再进行强转,因为进行了类型补偿。
- 泛型补偿告诉了类装载器,编译时期检查代码没有类型转换问题,使用补偿程序,根据元素获取其类型然后对迭代时期的元素进行类型转换(自动完成)。元素类型可以使用getClass()方法获取字节码文件对象,然后获取元素类型。该补偿机制自动执行。
- 泛型保证了元素类型的统一,所以插入的时候不可能插入不同类型的数据。也就保证了泛型补偿的时候不会出现类型转换异常。(只有固定的类型)
- 多用于集合框架,在该框架下,很多容器无法明确传入对象类型,所以使用泛型来明确。
- 好处
- 将运行时期的问题ClassCastException转到了编译时期
- 避免了迭代时强制转换的麻烦。
- 小知识
- Java分两个部分,编译和运行。
- 类加载器:也叫类装载器,JVM启动,读取并解析类文件,将类文件加载到内存。
- idea查看源代码都是从src.zip中查看的,这个文件移动了位置,就不能看了。可以更改该文件的位置。
- 对于字符的范围判断可以直接使用if (myChar >= 'a' && myChar <= 'z')来进行判断,就不必用数字来进行判断了。这种情况下,可以看作数字和字符是一种效果。
- TreeSet对于泛型的使用
TreeSet<Person> ts = new TreeSet<Person>();//创建容器的时候就明确传入类型
Iterator<Person> it = ts.iterator(); //使用迭代器的时候,直接接收已经明确类型的数据。避免后边再继续写强转代码
while(it.hasNext()){
Person p = it.next();//泛型自动补偿,不需要写强转,默认强转
System.out.println(p.getName);
}
public class Person implements Comparable<Person>//TreeSet会需要元素默认具有比较功能,实现Comparable的时候,加上泛型
public int compareTo(Person o){}/*接口加入泛型后,参数中会自动传入该类型。否则会传入Object类型,还需要进行强转。*/
- 泛型什么时候用?
- 用于操作的引用数据类型不确定的时候。传入要操作的引用数据类型即可。相当于定义参数类型的范围
- 用到了带<>的类或接口,就传入具体引用数据类型。
- 注意
- 泛型不能用基本数据类型,int不行,但是可以传入类,接口,数组(int[])。需要传入基本数据类型,就用包装类
- 当集合(ArrayList)已经明确了存入的是String类型ArrayList<String>,那么使用迭代器取出数据的时候,迭代器类型就可以直接使用泛型来接收
(Iterator<String>),而不用在迭代器迭代的时候再进行类型转换。
- 泛型自定义演示
- 用于操作很多种对象,例如操作猫,狗,可以统一操作为动物。
- 没有泛型的操作
public class Tool {/*工具类*/
private Object person;
public Object getPerson() {
return person;
}
public void setPerson(Object person) {
this.person = person;
}
public Tool(Object person) {
this.person = person;
}
public Tool() {
}
}
public static void main(String[] args) {/*主函数*/
Tool tool = new Tool();
tool.setPerson(new Student());
Student stu = (Student)tool.getPerson();/*不用泛型,这里需要进行强转,存的时候已经向上造型为Object*/
Tool tool1 = new Tool();
tool.setPerson(new Worker());
Student stu1 = (Student) tool.getPerson();/*强转可能出错,因为可以放入Object类型数据,传入Worker无误,但强转有问题。*/
}
-
- 自定义泛型类与方法演示
- 用于操作中的引用数据类型不确定的时候
- 可以操作多种传入的对象。
- 自定义泛型类与方法演示
public class Tool<MyTest> {/*工具类,加入了泛型*/
private MyTest q;
public Tool(MyTest q) {
this.q = q;
}
public Tool() {
}
public MyTest getQ() {
return q;
}
public void setQ(MyTest q) {
this.q = q;
}
/*泛型方法演示*/
public void print(MyTest q){
System.out.println(q);/*这个相当于是泛型的方法使用,类上泛型中传入了什么样的类型,这里就操作什么样的类型。提高了扩展性。*/
}
/*通过Object操作所有对象,达到扩展的功能*/
public void show1(Object q){
System.out.println("show1:" + q);
}
/*这里相当于将泛型定义在了方法上,传入任何对象都可以进行操作,和Object实现的效果类似*/
public <Animal> void show1(Animal q){
System.out.println("show1:" + q);
}
/*
方法为静态时,不能访问类上定义的泛型。因为静态不需要对象,泛型需要用对象进行明确new Tool<String>()
如果想要在静态方法上使用泛型就只能将泛型定义在方法上
*/
public static void method(MyTest q){
System.out.println("print:" + q);/*这里会出错*/
}
/*
在静态方法上定义泛型,将可以实现泛型的使用。泛型放到修饰符之后,返回值之前。
泛型传入的对象只能够使用Object对象的方法,因为这些方法是所有对象共有的。
*/
public static <Y> void methodNew(Y q){
System.out.println("print:" + q);
}
}
public static void main(String[] args) {
Tool<Student> tool = new Tool<Student>();
tool.setQ(new Worker());/*因为上边指定Student为使用的类,所以这里传入Worker(),将报错。类型检查没过去。*/
/*
1 用了泛型,不需要再进行强转
2 如果传入的对象类型和泛型指定的类型不一致会报错,降低运行时错误
*/
Student stu = tool.getQ();
}
-
- 泛型接口
/*使用的泛型需要定义到接口上,所以是泛型接口,这个泛型T相当于用一个参数来接收传进来的数据类型*/
interface Inter<T>{
public void show(T t);/*在使用方法的时候,不知道会传入什么类型的数据,所以使用泛型。*/
}
/*实现了接口,这里就知道需要什么类型的数据了,所以将泛型换成具体数据类型*/
class InterImpl implements Inter<String>{
public void show(String s) {
System.out.println(s);
}
}
/*实现了泛型接口之后,仍旧不知道会传入什么类型的数据,那么该类仍旧使用泛型接收数据,再将泛型传给接口。创建对象的时候可以传入实际数据类型。*/
class InterImpl2<Q> implements Inter<Q>{
public void show(String s) {
System.out.println(s);
}
}
-
- 泛型的通配符
- 小技巧:如何需要同时对ArrayList LinkedList HashSet等类进行相同操作,可以使用他们的共同父类Collection进行操作。这样会提高扩展性
- 泛型的通配符
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc");
al.add("hehe");
HashSet<String> a2 = new HashSet<String>();
al.add("abc1");
al.add("hehe1");
printCollection(al);
printCollection(a2);
}
public static void printCollection(Collection<String> al) {
Iterator<String> it = al.iterator();
while(it.hasNext()){
String item = it.next();
System.out.println(item);
}
}
-
-
- 泛型通配符的使用(用于传入未知类型,可能会传入String,Integer,Person或更多类型)
-
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc");
al.add("hehe");
HashSet<Integer> a2 = new HashSet<Integer>();
a2.add(123);
a2.add(312);
printCollection(al);
printCollection(a2);
}
/*
加上?是因为不明确传入的类型,也不需要对传入数据进行太多操作。
加上?相当于是<? extends Object>
也可以不加<?>,,直接Collection用来接收所有类型的数据,兼容老版本Java,但建议写上
*/
public static void printCollection(Collection<?> al) {
Iterator<?> it = al.iterator();
while(it.hasNext()){
System.out.println(it.next());/*这里只能直接操作,因为不知道传入的是什么类型.但是无论传入什么类型的数据,都可以调用toString()进行打印*/
}
}
/*可以方法上加泛型,这样就可以进行更多的操作了,*/
public static <T> T printCollection1(Collection<T> al) {
Iterator<T> it = al.iterator();
while(it.hasNext()){
T t = it.next();/*可以用变量接收it.next()*/
System.out.println(t);
}
return t;/*相当于在不明确传入的是什么类型的数据的情况下,以原数据类型返回需要的数据。*/
}
-
-
- 使用<? extends E>可以接受E和其子类型,扩充了单个泛型,限定了通配符的所有泛型。上限!。
- 存的时候一般用上限。因为自己和子类都可以处理。都按上限类型运算,没有类型安全隐患。al.addAll(a2)子类元素添加到父类中
- 使用<? extends E>可以接受E和其子类型,扩充了单个泛型,限定了通配符的所有泛型。上限!。
-
Collection<Person> p1 = new ArrayList<Person>();//写泛型,要左右两边传入的泛型一致。否则传入的时候如果前边泛型限定了Person,那么后边容器应该能够同时放Student和Worker,但是容器里只放了Student,说明不符合。
Collection<? extends Person> p1 = new ArrayList<Student>();//直接写泛型没有继承关系,只能传入什么,接收什么,但是可以通过通配符进行泛型的限定 ? extends Person相当于传入很多种类型的数据,但是这些数据都是继承自Person的。
public static void printCollection(Collection<? extends Person> c){
Iterator<? extends Person> it = c.iterator();
while(it.hasNext()){
Person p = it.next();
System.out.println(p.getName() + ":" + p.getAge());
}
}
-
-
- 使用<? super E>可以接收E和其父类型,下限!。(用的少)
- TreeSet有该构造器TreeSet(Comparator<? super E> comparator),传父类比较器可以统一比较父类及其子类。
- 通常对集合中元素进行取出操作的时候,可以使用下限。
- 使用<? super E>可以接收E和其父类型,下限!。(用的少)
-
public static void printCollection(Collection<? super Student> c){
Iterator<? super Student> it = c.iterator();/*迭代器的泛型和Collection的泛型一致*/
while(it.hasNext()){
System.out.println(it.next());
}
}
集合框架1.5新特性
- Collection是单列集合的顶层接口,但是JDK1.5后Collection又继承自Iterable接口。
- Iterable接口的方法只有一个,iterator()。有这个方法是因为以后集合框架可能仍需扩展,但希望每个集合框架都能有iterator()方法来拥有迭代功能。
- JDK1.5后新添增强for循环(一般称为foreach),可以遍历Collection和数组
- for (类型 变量 : Collection||Array) {},简化了Iterator的遍历过程
- 增强for循环将迭代工作简化成了一个语句,一般只用于迭代,而Iterator在迭代的过程中还可以继续操作原Collection。增强for循环简化之后少了些功能。
- 传统for循环和增强for循环的比较
- 传统for循环可以完成对语句执行很多次,因为可以定义控制循环的增量和条件
- 增强for循环是一种简化形式,必须有被遍历的目标(数组或Collection单列集合,不可以直接获取Map的key和value,只能用,Map.Entry或者获取了key再获取value)
- 对于数组,如果仅遍历元素可以使用增强for循环,需要使用角标的操作只能用传统for循环。(增强for循环无法获得角标,自定义增量就不如使用传统for循环了)
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
for (String item : list) {
System.out.println(item); /*简化了书写,就像最开始Iterotor代替了Enumeration*/
}
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
- 函数可变参数
- Arrays.asList用过这个特性。
- 对于加法运算,如果传入10个数需要进行运算,可以传一个数组过去(数据多了就可以用数组来进行存储)。但这样很麻烦。
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4};
int sum = add(arr);
System.out.println("sum=" + sum);
}
public static int add(int[] operateNumbers) {
int result = 0;
for (int item : operateNumbers) {
result += item;
}
return result;
}
-
- 使用可变参数列表,里边可以传数组,也可以传不定量个数据(int... arr)。一个都不传相当于是给了他一个空数组
- 本质是一个数组,但接收的是数组中的元素。传入后将元素自动封装为数组。简化了书写。
public static int add(int[] operateNumbers) {
int firstAdd = newAdd(operateNumbers);
return newAdd(firstAdd, 1, 2, 4, 5);
}
public static int newAdd(int... arr) { /*...代表省略,有很多参数,int...相当于int[]的新写法,将创建数组和传数组过程简化,使其内部实现*/
int result = 0;
for (int item : arr) {
result += item;
}
return result;
}
-
- 注意:可变参数类型必须放在参数列表的结尾,因为读取顺序而产生问题。(数组因为传入的不是未知数量的参数,所以没关系)
正确 public static int test1(int a, int... arr) {} 这里相当于把第一个数算成a的,从第二个开始放到arr里
错误 public static int test2(int... arr, int a) {} 这里会把后边的数都当成int放到arr里
数组 public static int test3(int[] arr, int a) {}
错误 public static int newAdd(String... test, int... a) {} 不可以放多种可变参数列表,只能放一种
- 静态导入
- 如果使用方法的时候觉得Collections.sort的Collections写起来也很费劲,可以将该方法直接导入进来
- 静态导入导进来的的是静态成员(静态方法,静态变量都可以)
- 如果方法名和其他类的方法名冲突,类名就不可省略了。
- 一般可以不使用,使用了之后,反而隐藏了方法使用的层级关系,减少了阅读性
import static java.util.Collections.sort; /*导入静态的方法,或者import static java.util.Collections.* */
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc4");
list.add("abc2");
list.add("abc3");
System.out.println(list);
sort(list);/*使用的时候不需要再加上Collections就可以直接进行使用了*/
System.out.println(list);
}
-
- 也可以用于System.out.println()的操作
import static java.lang.System.*; /*使用其中的方法的时候可以省略System前缀,println()由一个对象操纵,out持有该对象,所以只能简化到out*/
常用容器
- 数组(用于存大量相同数据,但只能存固定长度)
- StringBuffer(用于大量数据转为一整个字符串的操作)
- 对象(可用于存不同类型的数据,但只能存固定类型和固定含义的数据)
- 集合(可用于存很多的对象,而且可以存可变长度)
题目
- 获取一个字符串中,某子串出现次数。
- 准备
- 使用substring()获取每一次判断完剩下的字符串(会创建一堆字符串对象)
- 使用indexOf()从查询到后边的位置继续查询
- 使用子串进行切割(需要判断首尾有没有该字串,因为首尾不会切割出空串)
- 思路
- 用大圈套小圈,外层的大圈循环取几个字符(所有字符可以不取),内层的小圈每次取一个子串
- 用indexOf从开始找是否有匹配(-1则没有),有匹配的话则从匹配的位置往后数所取字符个数个位置继续找,直到到最后一个位置或-1
- 准备
- 获取两个字符串中最大相同的子串
- 思路
- 找到短的那个字符串
- 从短字符串中依次获取子串,查看是否包含在另一个字符串中(子串从长取到短)
- 思路
- 包装类的题
Integer a = new Integer(3);
Integer b = new Integer(3);
System.out.println(a == b);//false,两个对象
System.out.println(a.equals(b));//true,值相等
Integer x = 3;
Integer y = 3;
System.out.println(x == y);//true,jdk1.5后,自动装箱,如果装箱的是一个字节,该数据会被共享,不会新开辟空间
System.out.println(x.equals(y));//true
Integer x1 = 129;
Integer y1 = 129;
System.out.println(x1 == y1);//false,长度超过一个字节
System.out.println(x1.equals(y1));//true
- 使用LinkedList模拟堆栈(FILO)或者队列(FIFO)数据结构
- 需要描述一个容器,给用户提供这个容器,来完成这个效果
/*队列*/
class QueueDemo{
private LinkedList link;
QueueDemo(){
link = new LinkedList();
}
public void myAdd(Object obj){
link.addLast(obj);
}
public Object myGet(){
return link.removeFirst();/*需要用removeFirst(),用getFirst()无法获取下一个*/
}
public boolean isNull(){
return link.isEmpty();
}
}
/*堆栈*/
class StackDemo{
private LinkedList link;
StackDemo(){
link = new LinkedList();
}
public void myAdd(Object obj){
link.addLast(obj);
}
public Object myGet(){
return link.removeLast();/*需要用removeFirst(),用getFirst()无法获取下一个*/
}
public boolean isNull(){
return link.isEmpty();
}
}
- 去除ArrayList中的重复元素
- 注意在存自定义对象的时候,需要重写equals方法,因为ArrayList对于存入,删除或者是否包含某对象是通过直接调用equals来进行判断的。重写equals方法就可以自定义自己的判断方式了。例如判断Person对象中name和age相同即是相同元素,这样就可以在equals中操作了。
- 在TreeSet中对字符串进行长度排序。
- 自然排序(String自带的compareTo方法不符合要求)无法实现,则使用比较器进行比较
/*重写比较器的compare方法*/
public int compare(Object o1, Object o2) {
String s1 = (String)o1;
String s2 = (String)o2;
int temp = s1.length() - s2.length();
return temp == 0 ? s1.compareTo(s2):temp;
}
/*创建TreeSet对象的时候传入该比较器,添加元素后将按照比较器的方法比较*/
TreeSet ts = new TreeSet(new ComparatorByLength());
- “fdgavcbsacdfs”获取该字符串中,每一个字母出现的次数。(Map的使用),要求打印结果是a(2)b(1)
- 这里一个是字母,一个是次数,相当于字母与次数有映射关系。且关系数很多
- 当前容器中数组和Map集合有映射关系,可以做这件事。数组用于映射关系的一方为有序编号,Map用于映射关系的两方都没有有序编号
- 使用Map集合发现,保证唯一性的一方具备顺序,因而使用TreeMap集合
public static void main(String[] args) {
String tempStr = "fdgavcbsacdfs";
String s = getCharCount(tempStr);
System.out.println(s);
}
private static String getCharCount(String tempStr) {
/* 只接收字母 */
if (!(charsInTempStr[i] >= 'a' && charsInTempStr[i] <= 'z' || charsInTempStr[i] >= 'A' && charsInTempStr[i] <= 'Z'))
continue;
/* 将字符串变成字符数组 */
char[] charsInTempStr = tempStr.toCharArray();
/* 定义Map集合表 */
Map<Character, Integer> map = new TreeMap<Character, Integer>();
for (int i = 0; i < charsInTempStr.length; i++) {
/* 用数组的字母查表 */
Integer value = map.get(charsInTempStr[i]);/* 这里使用了包装类,如果返回null,也是可以接收结果的,省去了使用cotainsKey的过程 */
/* 判断值是否为空 */
/*
if (value == null) {
map.put(charsInTempStr[i], 1);// 代码在重复使用,可以改进
} else {
map.put(charsInTempStr[i], ++value);
}
*/
int count = 1;
if(value != null)
count = value + 1;
map.put(charsInTempStr[i], count);
}
return mapToString(map);
}
private static String mapToString(Map<Character, Integer> map) {
Iterator<Map.Entry<Character, Integer>> it = map.entrySet().iterator();
StringBuilder sb = new StringBuilder();/* 使用大量的字符串使用StringBulider */
while (it.hasNext()) {
Map.Entry<Character, Integer> item = it.next();
sb.append(item.getKey() + "(" + item.getValue() + ")");
}
return sb.toString();
}