admin管理员组

文章数量:1579086

java第五周总结

正则表达式补充

group

捕获组通过从左到右计算其开始括号进行编号。 例如,在表达式((A)(B(C)))中,存在四个这样的组:

  1. ((A)(B(C)))
  2. (A)
  3. (B(C))
  4. (C)

使用方式:

String score = "109:95";

String regex = "((\\d{1,3}):(\\d{1,3}))";

Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(score);
//搜索整个字符串序列
m.find();

System.out.println(m.group());  //109:95
System.out.println(m.group(0)); //109:95
System.out.println(m.group(1)); //109:95
System.out.println(m.group(2)); //109
System.out.println(m.group(3)); //95

扩展:

//火箭队:湖人队/勇士队:骑士队/热火队:凯尔特人队
score = “95:98/97:99/95:88”;
//分别获取三组比分,以及每一个队伍的得分

//火箭队:湖人队/勇士队:骑士队/热火队:凯尔特人队
String score = "95:98/97:99/95:88";
//分别获取三组比分,以及每一个队伍的得分
String regex = "((\\d{1,3}):(\\d{1,3}))";

Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(score);
while(m.find()) {
    System.out.println(m.group(1));
    System.out.println(m.group(2));
    System.out.println(m.group(3));	
    System.out.println("==========");
}

运行结果:

95:98
95
98
==========
97:99
97
99
==========
95:88
95
88
==========

正则表达式三种模式

贪婪模式(greedy)

贪婪模式即从匹配到的位置开始一直往后依次搜索,并且会回溯

String html = "href=\"//cloud.video.taobao/video/1098/aaabbc.swf\" href=\"http://www.softeem/video/aaa.swf\"";
Pattern pMp4 = Pattern.compile("//cloud.video.taobao.+.swf");
Matcher mMp4 = pMp4.matcher(html);
while(mMp4.find()) {
    System.out.println(mMp4.group());
}

结果:

//cloud.video.taobao/video/1098/aaabbc.swf" href="http://www.softeem/video/aaa.swf
懒惰模式(reluctant)

通过贪婪模式能够发现,表达式会一直往后搜索,以最后一个匹配到的结尾为终止条件,获取的结果跟预期的存在差距,只需要将正则表达式做如下修改即可匹配到我们需要的资源:

//cloud.video.taobao.+?.swf

以上的匹配模式称之为懒惰匹配(勉强模式),寻找最短的匹配

结果:

//cloud.video.taobao/video/1098/aaabbc.swf
//cloud.video.taobao/video/aaa.swf
独占模式(possessive)

独占模式跟贪婪模式的区别在于,不会回溯,即一直往后搜索会将后续的所有字符串进行匹配

String s = "aabbbccddcaabbbdd";
String regex = "aa.*+dd";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
m.find();
System.out.println(m.group());

结果:

Exception in thread "main" java.lang.IllegalStateException: No match found
	at java.util.regex.Matcher.group(Unknown Source)
	at java.util.regex.Matcher.group(Unknown Source)
	at com.softeem.lesson20.regex.ReluctantDemo.main(ReluctantDemo.java:27)

集合入门

在java基础阶段,能存储大量相同数据的容器只有数组,但是是数组存在一个弊端,必须指定数组的容量,而且容量无法改变(即数组的长度一旦定义则无法修改),因此我们学习了ArrayList。在jdk1.2之前java官方就提供了一些集合的结局方案:

  • Enumeration(枚举:对集合迭代接口)
  • Vector(向量)
  • Stack(栈)
  • Hashtable(哈希表)
  • Dictionary(字典)
  • Properties(属性表)

以上集合工具在jdk1.2之前就已经存在,但是由于没有一个统一的标准,因此组织混乱,而且也存在部分bug.

从JDK1.2开始java中新增了集合API,用于将所有集合进行统一归纳,形成了两种集合的解决方案:

  • 单例集合
  • 双列集合

单列集合有一个顶层的接口:Collection

双列集合有一个顶层接口:Map

Collection

Collection是所有单列集合的顶层接口,在java中存在的有序集合(List)和无序集合(Set)接口都从Collection接口继承,Collection中的常用方法有:

  • public boolean add(E e): 把给定的对象添加到当前集合中 。
  • public boolean addAll(Collection e): 把给定的集合对象添加到当前集合中 。
  • public void clear() :清空集合中所有的元素。
  • public boolean remove(E e): 把给定的对象在当前集合中删除。
  • public boolean contains(E e): 判断当前集合中是否包含给定的对象。
  • public boolean isEmpty(): 判断当前集合是否为空。
  • public int size(): 返回集合中元素的个数。
  • public Object[] toArray(): 把集合中的元素,存储到数组中。
  • public Iterator iterator():获取当前集合的迭代器对象
  • default Stream stream():获取用于进行流式处理的Stream对象(JDK8新增)

由于Collection是一顶层集合接口,因此对于不同类型的集合也存在两个子接口分别进行处理:

  • List:是一个有序的集合,并且允许重复的元素出现
  • Set:是一个无序集合,并且不允许重复元素出现

面试题:

Collection、Collections、Connection什么区别?

Collection是所有单列集合的顶层接口;Collections是针对集合进行处理的工具类,比如排序,查找,洗牌,逆序等操作;Connection是java访问数据库技术(JDBC)中的数据库连对象的顶层接口

List集合

List接口是一个有序的集合,内部允许重复(e1.equals(e2))的元素出现,并且元素的存储顺序是按照添加顺序存储,因此可以通过元素的索引位置快捷的搜索到目标元素;List接口除了包含Collection中的方法之外,还新增了以下常见方法:

  • public E get(int index):根据元素的索引获取指定位置的元素并返回
  • public ListIterator listIterator(): 获取此集合对应的列表(有序)迭代器
  • public E remove(int index):移除指定位置的元素
  • public List subList(int fromIndex,int toIndex):将集合从指定位置进行截取,截取到目标位置,并将返回的数据形成新子List(假分页)

List接口有几个常见的实现类:

  • ArrayList
  • LinkedList
  • Vector

ArrayList

其中最常用的是java.util.ArrayList;ArrayList内部基于数组+数据拷贝的实现,初始容量是10,当添加的元素位置超出容量时,会在原数组的容量基础上扩充为1.5倍;由于ArrayList是基于数组的实现,因此在进行数据检索时的效率很高,只需要获取到元素的索引就能快速定位到元素的位置,但是由于数组的长度一旦定义,则无法修改,因此在对ArrayList进行元素的添加和删除的时候会导致数组的容量发生变化,需要频繁的创建新数组的对象,因此在进行添加,删除时效率很低;ArrayList适合做查询不适合做修改(查快改慢)

ArrayList基本使用:

List list = new ArrayList();
list.add(new String("hello"));
list.add(true);
list.add(100);
list.add('h');

//向指定的位置插入元素
list.add(1,"world");

System.out.println("集合中是否包含指定元素:"+list.contains("hello"));

//替换指定位置的元素
list.set(1, "softeem");

//截取一个子集合(前包后不包)
list= list.subList(2, 5);

System.out.println(list.toString());
//将集合转换为对象数组
Object[] objs = list.toArray();
System.out.println(objs.length);

//清空集合
list.clear();
System.out.println("集合是否为空:"+list.isEmpty());

LinkedList

​ 前面已经了解了ArrayList的实现原理是基于数组结合数组拷贝,但是由于数组的先天性问题:长度一旦定义无法修改,因此ArryList不适合进行频繁的修改(增加,删除)操作;如果需要频繁对容器进行修改操作时,List接口还提供了另一个实现类:LinkedList;

​ LinkedList是基于双向链表的实现(链表的结构如下),在元素进行增删操作时,只需要修改链表的首尾指针即可轻松实现,因此LinkedList适合用于频繁的修改操作中;但是在进行元素的检索时,只能从链表头,链表尾部依次搜索,查询效率相对较低。(LinkedList改快查慢

常用方法:

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可:

  • public void addFirst(E e):将指定元素插入此列表的开头。
  • public void addLast(E e):将指定元素添加到此列表的结尾。
  • public E getFirst():返回此列表的第一个元素。
  • public E getLast():返回此列表的最后一个元素。
  • public E removeFirst():移除并返回此列表的第一个元素。
  • public E removeLast():移除并返回此列表的最后一个元素。
  • public E pop():从此列表所表示的堆栈处弹出一个元素。
  • public void push(E e):将元素推入此列表所表示的堆栈。
  • public boolean isEmpty():如果列表不包含元素,则返回true。

LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。(了解即可)

使用参考:

LinkedList list = new LinkedList();
list.add("rose");
list.add("jack");
list.add("bob");
list.add("tom");
list.add("jarry");
list.add("bob");
list.add(null);

//这里的数值不是索引,表示的链表需要进行节点搜索的次数
System.out.println(list.get(5));

for (Object obj : list) {
    System.out.println("--->"+obj);
}
System.out.println(list.size());

//向集合的头部插入元素
list.addFirst("123");
//向集合的尾部插入元素
list.addLast("789");
System.out.println(list.getFirst()+"获取头部元素");
System.out.println(list.getLast()+"获取尾部元素");

//从头部弹出元素(将元素从集合中删除)
System.out.println(list.pop());
//向集合中加入元素(等同addFirst)
list.push("456");
System.out.println(list);

Vector

​ Vector是在JDK1.0就已经存在的基于动态数组(数组+数组拷贝)的集合实现,Vector是一个线程安全(关键方法上都使用了synchronized修饰)的集合实现,Vector在容量扩充时,增长为原理啊的2倍(ArrayList是1.5倍);在jdk1.2之后新的集合API出现,Vector被改造为从List集合实现,因此Vector,ArrayList,LinkedList都是源自于List接口

Vector,LinkedList,ArrayList区别?

  • Vector是老式的集合类,内部基于动态数组实现,容量扩充为原来的2倍,Vector是线程安全的实现
  • ArrayList是JDK1.2之后新增的List集合的实现,内部也是基于动态数组实现,容量扩展为原来的1.5倍,ArrayList是线程不安全的实现(效率高),数据查询较快,修改较慢
  • LinkeadList是JDK1.2之后新增的List集合的实现,内部是基于双向链表实现,也是线程不安全的实现,在进行数据修改方面比较快,数据查询较慢

Iterator(迭代器)

Iterator接口从jdk1.5之后新增的对集合进行快速遍历的接口,内部实现通常是由集合是类自身通过内部类的形式来完成,比如ArrayList:

常用方法

  • public boolean hashNext():判断迭代器是否存在下一个可以迭代的元素
  • public E next():获取下一个迭代到的元素
  • public void remove():删除当前迭代到的元素

基本使用方式:

List list = new ArrayList();
list.add("lilei");
list.add("lily");
list.add("lucy");
list.add("rose");
list.add("jack");
//获取集合自身的迭代器对象
Iterator it = list.iterator();
while(it.hasNext()) {
    Object obj = it.next();
    it.remove();
}

由于以上语法相对比较复杂,因此迭代器的出现同时也新增一种语法糖技术:forEach

for(Object obj:list){
    System.out.println(obj);
}

ListIterator

ListIterator从Iterator实现而来,在Iterator的基础上扩充了一些方法:

  • public boolean hasPreviouse():是否存在上一个可迭代元素
  • public E previouse():获取上一个迭代到的元素
  • public void add(E e):通过迭代器向元素中添加一个元素
  • public void set(E e):通过迭代器,修改当前位置的元素
List list = new ArrayList();
list.add("helloworld");
list.add("jack");
list.add("rose");
list.add("lily");
list.add("lucy");

//获取当前集合的列表迭代器(可以反向迭代,添加,替换元素)
ListIterator it = list.listIterator();
//循环判断是否有下一个可迭代元素
while(it.hasNext()) {
    //取出下一个元素
    System.out.println(it.next());
}
//添加元素
it.add("kobe");
System.out.println("=========");
//循环判断是否有上一个可迭代元素
while(it.hasPrevious()) {
    //取出上一个元素
    System.out.println(it.previous());
}

面试问题:

在对集合元素遍历的同时进行删除或者添加操作时会导致一个异常出现:ConcurrentModifactionException,导致原因是因为在集合内部的每一个更新方法中都包含一行代码modCount++

在获取迭代器时会首先将改值缓存到Iterator对象的成员变量中

解决方案:

  1. 使用迭代器自身的remove
Iterator it = list.iterator();
while(it.hasNext()) {
    Object obj = it.next();
    it.remove();
}
  1. 在找到满足条件的元素进行操作之后,立马结束循环

    for (Object obj : list) {
        if("lucy".equals(obj)) {				
            list.remove(obj);
            //结束循环
            break;
        }
    }
    
  2. 当需要更改的元素不止一个时可以,使用如下方式:

    //声明临时数组
    List temp = new ArrayList();
    for (Object obj : list) {
        if("lucy".equals(obj) || "lily".equals(obj)) {	
            //将符合条件的元素存储到临时集合中
            temp.add(obj);
        }
    }
    //在循环结束后统一处理
    list.removeAll(temp);
    

练习题

  1. 完成一个通讯录,需求:

    (1)添加联系人(联系人:编号,姓名,手机号,QQ,邮箱地址)添加时需要检查手机号和邮箱地址格式是否正确,若不正确,不允许添加

    (2)联系人查询(输入姓名或电话查询)

    (3)显示联系人列表

    (4)根据编号删除指定编号的联系人
    联系人类

package com.softeem.lesson21.example;

/**
 * 联系人
 * @author 
 */
public class Contact {

	private int num;
	private String name;
	private String phone;
	private String qq;
	private String email;
	
	public Contact() {
		// TODO Auto-generated constructor stub
	}

	public Contact(int num, String name, String phone, String qq, String email) {
		super();
		this.num = num;
		this.name = name;
		this.phone = phone;
		this.qq = qq;
		this.email = email;
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getQq() {
		return qq;
	}

	public void setQq(String qq) {
		this.qq = qq;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Override
	public String toString() {
		return "Contact [num=" + num + ", name=" + name + ", phone=" + phone + ", qq=" + qq + ", email=" + email + "]";
	}
	
}

联系人管理类

package com.softeem.lesson21.example;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 联系人管理类
 * 
 * @author 
 */
public class ContactManager {

	/**
	 * 存储所有联系人信息
	 */
	private static ArrayList<Contact> list = new ArrayList<>();

	/**
	 * 添加联系人
	 * 
	 * @param c
	 * @return
	 */
	public boolean add(Contact c) {
		// ctrl+1
		String phone = c.getPhone();
		String qq = c.getQq();
		String email = c.getEmail();
		if (!phone.matches("^1\\d{10}$")) {
			System.out.println("手机号格式不正确!");
			return false;
		}
		if (!qq.matches("^[1-9]\\d{4,11}$")) {
			System.out.println("QQ号格式不正确");
			return false;
		}
		if (!email.matches("^\\w+?@.+?\\.\\w+$")) {
			System.out.println("邮箱格式不匹配");
			return false;
		}
		list.add(c);
		return true;
	}

	/**
	 * 根据条件查询(模糊查询)
	 * 
	 * @param flag 查询方式:1-按姓名 2-按手机号
	 * @param key  关键字
	 * @return
	 */
	public List<Contact> findByCondition(int flag, String key) {
		// 声明临时变量存储查询到符合条件的数据
		ArrayList<Contact> temp = new ArrayList<Contact>();
		// 对联系人遍历
		for (Contact c : list) {
			if (flag == 1) {
				// 按姓名(判断联系人姓名中是否包含指定的查询关键字)
				if (c.getName().contains(key)) {
					temp.add(c);
				}
			} else if (flag == 2) {
				// 按手机号
				if (c.getPhone().contains(key)) {
					temp.add(c);
				}
			} else {
				temp = list;
				break;
			}
		}
		return temp;
	}

	/**
	 * 查询所有(分页)
	 * @param pageNow  当前数据的页码数
	 * @param pageSize 每页数据的最大条目
	 * @return 返回当前页的数据集
	 */
	public List<Contact> findAll(int pageNow, int pageSize) {
		//计算起始位置
		int from = (pageNow - 1) * pageSize;
		int to = pageNow * pageSize;
		//如果集合中没有数据时返回null
		if(list.size() < 1) {
			return null;
		}
		//开始位置不能等于总数据条数(越界)
		if(from >= list.size()) {
			return null;
		}
		//结尾的位置不能超过数据总条数
		if(to > list.size()) {
			to = list.size();
		}
		return list.subList(from, to);
	}
	
	/**
	 * 根据编号删除联系人
	 * @param num
	 * @return
	 */
	public boolean del(int num) {
		Contact target = null;
		for(Contact c:list) {
			if(c.getNum() == num) {
				target = c;
				break;
			}
		}
		if(Objects.isNull(target)) {
			System.out.println("未找到目标联系人");
			return false;
		}
		return list.remove(target);
	}

}

客户端

package com.softeem.lesson21.example;

import java.util.List;
import java.util.Objects;
import java.util.Scanner;

public class Client {
	
	Scanner input = new Scanner(System.in);
	ContactManager cm = new ContactManager();
	
	public void mainMenu() {
		p("**********欢迎使用SOFTEEM-SMART通信录*******");
		p("*\t[1]添加联系人\t\t\t*");
		p("*\t[2]联系人列表\t\t\t*");
		p("*\t[3]根据姓名查询联系人\t\t*");
		p("*\t[4]根据手机号查询联系人\t\t*");
		p("*\t[5]删除联系人\t\t\t*");
		p("*\t[0]退出\t\t\t\t*");
		p("*****************************************");
		p("请输入正确的操作指令:");
		//开始
		start();
	}
	
	public void start() {
		String s = input.next();
		if(!s.matches("^[0-5]$")) {
			p("请输入合法的操作指令");
			start();
		}
		switch(s) {
		case "1":
			//添加联系人
			addContact();
			break;
		case "2":
			//联系人列表
			listContact();
			break;
		case "3":
			//查询联系人(by 姓名)
			queryByCondition(1);
			break;
		case "4":
			//查询联系人(by 手机号)
			queryByCondition(2);
			break;
		case "5":
			//删除联系人
			delByNum();
			break;
		case "0":
			//退出
			p("谢谢使用,再见!");
			System.exit(0);
			break;
		}
	}
	
	/**
	 * 删除联系人
	 */
	private void delByNum() {
		p("请输入联系人的编号:");
		String num = input.next();
		if(!num.matches("^\\d+$")) {
			p("请输入正确的编号");
			delByNum();
		}else {
			boolean f = cm.del(Integer.parseInt(num));
			if(f) {
				p("删除成功");
			}
			mainMenu();
		}
	}

	/**
	 * 条件查询
	 * @param flag
	 */
	private void queryByCondition(int flag) {
		p("请输入查询关键字");
		String key = input.next();
		List<Contact> list = cm.findByCondition(flag, key);
		//判断集合是否为空
		if(list.isEmpty()) {
			p("未查询到匹配的联系人!");
		}else {
			for(Contact c:list) {
				p(c);
			}
		}
		mainMenu();
	}

	/**
	 * 联系人列表
	 */
	private void listContact() {
		p("请输入需要显示的联系人页码数和每页条数(按:页码/数据条数):");
		String s = input.next();
		if(!s.matches("\\d{1,3}/\\d{1,3}")) {
			System.out.println("输入的页码或每页条数格式不正确");
			listContact();
		}else {
			String[] nums = s.split("/");
			List<Contact> list = cm.findAll(Integer.parseInt(nums[0]), Integer.parseInt(nums[1]));
			if(Objects.isNull(list)) {
				p("没有更多数据可显示!");
				mainMenu();
			}else {
				for (Contact c : list) {
					p(c); 
				}
				listContact();
			}
		} 
	}

	/**
	 * 添加
	 */
	private void addContact() {
		p("请输入联系人信息(按:编号/姓名/手机号/qq/邮箱)");
		String s = input.next();
		if(!s.matches("\\d+/.+/1\\d{10}/[1-9]\\d{4,11}/\\w+?@.+?\\.\\w+")) {
			System.out.println("输入信息有误,请重新输入");
			addContact();
		}else {			
			String[] info = s.split("/");
			//根据输入的信息构建联系人对象
			Contact c = new Contact(Integer.parseInt(info[0]), info[1], info[2], info[3], info[4]);
			if(cm.add(c)) {
				p("添加成功!");
				mainMenu();
			}else {
				addContact();
			}
		}
	}

	public void p(Object msg) {
		System.out.println(msg);
	}

	public static void main(String[] args) {
		new Client().mainMenu();
	}

}

结果:

本文标签: Java