黑马程序员--集合

                   ------------android培训java培训、期待与您交流! --------------


大纲:

       一、集合概念

        二、Collection集合框架

        三、List列表概述

        四、LinkedList列表

       五、用LInkedList模拟队列、堆栈

       六、ArrayList列表

       七、set集合概述

       八、HashSet集合

       九、TreeSet集合

       十、Map集合概述

       十一、HashMap集合

       十二、TreeMap集合

一、集合概念

集合是用来存放对象的,数据多了用对象存,对象多了用集合存,数组虽然也可以存储对象,但长度是固定的

而集合长度是可变的。数组只能存储同一种类型的数据,而集合可以存储不同类型的数据;数组中可以存储基

本数据类型,集合只能存储对象。(注意:集合中存的只是对象的内存地址,并不是对象实体)


二、Collection集合框架



Collection定义了集合框架的共性功能:
1,添加
add(e);
addAll(collection);
2,删除
remove(e);
removeAll(collection);
clear();
3,判断。
contains(e);
isEmpty();
4,获取
iterator();
size();
5,获取交集。
retainAll();
6,集合变数组。
toArray();
1,add方法的参数类型是Object。以便于接收任意类型对象。
2,集合中存储的都是对象的引用(地址)


Collection体系下的集合特点:
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快,但是增删稍慢,线程不同步,若要同步自己加锁。
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。

|--Set:元素是无序,元素不可以重复。

|--HashSet:底层数据结构是哈希表。是线程不安全的,不同步的。

|--
迭代器概念:

其实就是集合的取出元素的方式。把取出方式定义在集合内部,这样

取出方式就可以直接访问集合内容的元素。那么取出方式就被定义成

内部类。而每一个容器的数据结构不同,所以取出的动作细节也不一样

但是都有共性内容判断和取出。那么可以将共性进行抽取。 

通过容器的iterator()方法获取该内部类的对象。

示例代码1(集合的基本操作):

import java.util.*;
class  CollectionDemo
{
	public static void main(String[] args) 
	{
		
		method_get();
	}
	public static void method_get()
	{
		ArrayList al = new ArrayList();


		//1,添加元素。
		al.add("java01");//add(Object obj);
		al.add("java02");
		al.add("java03");
		al.add("java04");


		/*
		Iterator it = al.iterator();//获取迭代器,用于取出集合中的元素。


		while(it.hasNext())
		{
			sop(it.next());
		}
		*/


		for(Iterator it = al.iterator(); it.hasNext() ; )
		{
			sop(it.next());
		}
	}

	public static void method_2()
	{
		ArrayList al1 = new ArrayList();


		al1.add("java01");
		al1.add("java02");
		al1.add("java03");
		al1.add("java04");
		ArrayList al2 = new ArrayList();

		al2.add("java03");
		al2.add("java04");
		al2.add("java05");
		al2.add("java06");

		//al1.retainAll(al2);//去交集,al1中只会保留和al2中相同的元素。
		al1.removeAll(al2);


		sop("al1:"+al1);
		sop("al2:"+al2);

	}


	public static void base_method()
	{
		//创建一个集合容器。使用Collection接口的子类。ArrayList
		ArrayList al = new ArrayList();


		//1,添加元素。
		al.add("java01");//add(Object obj);
		al.add("java02");
		al.add("java03");
		al.add("java04");


		//打印原集合。
		sop("原集合:"+al);

		//3,删除元素。
		//al.remove("java02");
		//al.clear();//清空集合。

		//4,判断元素。
		sop("java03是否存在:"+al.contains("java03"));
		sop("集合是否为空?"+al.isEmpty());

		//2,获取个数。集合长度。
		sop("size:"+al.size());


		//打印改变后的集合。
		sop(al);


	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

三、List列表概述

1、List特有方法:

凡是可以操作角标的方法都是该体系特有的方法。

add(index,element);
addAll(index,Collection);

remove(index);

set(index,element);

get(index):
subList(from,to);
listIterator();
int indexOf(obj):获取指定元素的位置。
ListIterator listIterator();

2、List集合特有的迭代器:

ListIterator是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。
因为会发生ConcurrentModificationException异常。
所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,
只能对元素进行判断,取出,删除的操作,
如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。
该接口只能通过List集合的listIterator方法获取。


四、LinkedList列表

  链式存储结构,适用于频繁增删改,而不适用于频繁的查询。

LinkedList:特有方法:
addFirst();
addLast();

getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException

removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException

在JDK1.6出现了替代方法:

添加元素
offerFirst();
offerLast();

获取元素,但不删除元素。如果集合中没有元素,会返回null。
peekFirst();
peekLast();

获取元素,但是元素被删除。如果集合中没有元素,会返回null。
pollFirst();
pollLast();


示例代码2

class LinkedListDemo 
{
	public static void main(String[] args) 
	{
		LinkedList link = new LinkedList();

		link.addLast("java01");
		link.addLast("java02");
		link.addLast("java03");
		link.addLast("java04");

		//sop(link);
//		sop(link.getFirst());
//		sop(link.getFirst());
		//sop(link.getLast());
		//sop(link.removeFirst());
		//sop(link.removeFirst());

		//sop("size="+link.size());

		while(!link.isEmpty())
		{
			sop(link.removeLast());
		}

	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}


五、用LInkedList模拟队列、堆栈

使用LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出  如同一个杯子。
队列:先进先出 First in First out  FIFO 如同一个水管。

示例代码3(模拟队列、堆栈)

import java.util.*;
class DuiLie
{
	private LinkedList link;

	DuiLie()
	{
		link = new LinkedList();
	}
	
	public void myAdd(Object obj)
	{
		link.addFirst(obj);
	}
	public Object myGet()
	{
		return link.removeLast();//队列	
		return link.removeFirst();//堆栈
	}
	public boolean isNull()
	{
		return link.isEmpty();
	}

}

class  LinkedListTest
{
	public static void main(String[] args) 
	{
		DuiLie dl = new DuiLie();
		dl.myAdd("java01");
		dl.myAdd("java02");
		dl.myAdd("java03");
		dl.myAdd("java04");

		while(!dl.isNull())
		{
			System.out.println(dl.myGet());
		}
	}
}


六、ArrayList列表

顺序存储结构,适用于频繁查询,而不适用于频繁的增删改。

示例代码4(去重复元素1)

import java.util.*;
class ArrayListTest 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		ArrayList al = new ArrayList();
		al.add("java01");
		al.add("java02");
		al.add("java01");
		al.add("java02");
		al.add("java01");
//		al.add("java03");

		sop(al);		
		al = singleElement(al);
		sop(al);	
	}

	public static ArrayList singleElement(ArrayList al)
	{
		//定义一个临时容器。
		ArrayList newAl = new ArrayList();
		Iterator it = al.iterator();
		while(it.hasNext())
		{
			Object obj = it.next();
			if(!newAl.contains(obj))
				newAl.add(obj);
		}


		return newAl;
	}
}

示例代码5(去重复元素2)

将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。
思路:
1,对人描述,将数据封装进人对象。
2,定义容器,将人存入。
3,取出。
List集合判断元素是否相同,依据是元素的equals方法。

contains,remove方法内部调用的都是equals方法。

class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public boolean equals(Object obj)
	{

		if(!(obj instanceof Person))
			return false;

		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age == p.age;
	}

	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
}
class ArrayListTest2 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		ArrayList al = new ArrayList();

		al.add(new Demo());

		al.add(new Person("lisi01",30));
		//al.add(new Person("lisi02",32));
		al.add(new Person("lisi02",32));
		al.add(new Person("lisi04",35));
		al.add(new Person("lisi03",33));
		//al.add(new Person("lisi04",35));

		
		//al = singleElement(al);

		sop("remove 03 :"+al.remove(new Person("lisi03",33)));//remove方法底层也是依赖于元素的equals方法。
		Iterator it = al.iterator();
		while(it.hasNext())
		{
			Person p = (Person)it.next();
			sop(p.getName()+"::"+p.getAge());
		}
	}


	public static ArrayList singleElement(ArrayList al)
	{
		//定义一个临时容器。
		ArrayList newAl = new ArrayList();

		Iterator it = al.iterator();

		while(it.hasNext())
		{
			Object obj = it.next();

			if(!newAl.contains(obj))
				newAl.add(obj);

		}

		return newAl;
	}
}


七、set集合概述

|--Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
|--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。

|--TreeSet:可以对Set集合中的元素进行排序。
底层数据结构是二叉树。
保证元素唯一性的依据:
compareTo方法return 0.
Set集合的功能和Collection是一致的。


八、HashSet集合

底层是哈希表结构,通过hashCode和equals来确保元素唯一。

示例代码6(HashSet):

class HashSetTest 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		HashSet hs = new HashSet();

		hs.add(new Person("a1",11));
		hs.add(new Person("a2",12));
		hs.add(new Person("a3",13));
//		hs.add(new Person("a2",12));
//		hs.add(new Person("a4",14));

		//sop("a1:"+hs.contains(new Person("a2",12)));
			
//		hs.remove(new Person("a4",13));
		

		Iterator it = hs.iterator();

		while(it.hasNext())
		{
			Person p = (Person)it.next();
			sop(p.getName()+"::"+p.getAge());
		}
	}
}
class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public int hashCode()
	{
		System.out.println(this.name+"....hashCode");
		return name.hashCode()+age*37;
	}

	public boolean equals(Object obj)
	{

		if(!(obj instanceof Person))
			return false;

		Person p = (Person)obj;
		System.out.println(this.name+"...equals.."+p.name);

		return this.name.equals(p.name) && this.age == p.age;
	}

	
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
}

九、TreeSet集合

能够进行元素排序的集合,有自然排序和传入比较器两种方式,

以第二种方式最为常用。

1、TreeSet排序的第一种方式:

让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。
这种方式也称为元素的自然顺序,或者叫做默认顺序。

示例代码7(TreeSet第一种排序):

/*
往TreeSet集合中存储自定义对象学生。
想按照学生的年龄进行排序,如果年龄相同,需要判断姓名
排序时,当主要条件相同时,一定判断一下次要条件。

*/

class TreeSetDemo 
{
	public static void main(String[] args) 
	{
		TreeSet ts = new TreeSet();

		ts.add(new Student("lisi02",22));
		ts.add(new Student("lisi007",20));
		ts.add(new Student("lisi09",19));
		ts.add(new Student("lisi08",19));
		//ts.add(new Student("lisi007",20));
		//ts.add(new Student("lisi01",40));

		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}


class Student implements Comparable//该接口强制让学生具备比较性。
{
	private String name;
	private int age;

	Student(String name,int age)
	{
		this.name = name;
		this.age = age;
	}

	public int compareTo(Object obj)
	{

		//return 0;
		
		if(!(obj instanceof Student))
			throw new RuntimeException("不是学生对象");
		Student s = (Student)obj;

		System.out.println(this.name+"....compareto....."+s.name);
		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);
		}
		return -1;
		/**/
	}

	public String getName()
	{
		return name;

	}
	public int getAge()
	{
		return age;
	}
}

2、TreeSet排序的第二种方式:
当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
当两种排序都存在时,以比较器为主。
定义一个类,实现Comparator接口,覆盖compare方法。

示例代码8(TreeSet第二种排序):

class Student implements Comparable//该接口强制让学生具备比较性。
{
	private String name;
	private int age;

	Student(String name,int age)
	{
		this.name = name;
		this.age = age;
	}

	public int compareTo(Object obj)
	{

		if(!(obj instanceof Student))
			throw new RuntimeException("不是学生对象");
		Student s = (Student)obj;

		//System.out.println(this.name+"....compareto....."+s.name);
		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);
		}
		return -1;
		/**/
	}

	public String getName()
	{
		return name;

	}
	public int getAge()
	{
		return age;
	}
}
class TreeSetDemo2 
{
	public static void main(String[] args) 
	{
		TreeSet ts = new TreeSet();

		ts.add(new Student("lisi02",22));
		ts.add(new Student("lisi02",21));
		ts.add(new Student("lisi007",20));
		ts.add(new Student("lisi09",19));
		ts.add(new Student("lisi06",18));
		ts.add(new Student("lisi06",18));
		ts.add(new Student("lisi007",29));
		//ts.add(new Student("lisi007",20));
		//ts.add(new Student("lisi01",40));

		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}

class MyCompare implements Comparator
{
	public int compare(Object o1,Object o2)
	{
		Student s1 = (Student)o1;
		Student s2 = (Student)o2;

		int num = s1.getName().compareTo(s2.getName());
		if(num==0)
		{

			return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
			/*
			if(s1.getAge()>s2.getAge())
				return 1;
			if(s1.getAge()==s2.getAge())
				return 0;
			return -1;
			*/
		}		
		return num;

	}
}

结果是按照MyCompare比较器进行比较。

示例代码9(TreeSet练习):

/*"90 -7 0 18 2 45 4"

将字符串中的数值进行排序。使用TreeSet完成。

思路
1,将字符串切割。
2,可以将这些对象存入TreeSet集合。因为Integer自身具备排序功能。


*/
import java.util.*;
class TreeSetTest2 
{
	public static void main(String[] args) 
	{

		ArrayList al = new ArrayList();

		String str = "90 -7 0 18 2 45 4";

		String[] arr = str.split(" ");

		TreeSet ts = new TreeSet();

		for(int x=0; x<arr.length; x++)
		{
			//ts.add(new Integer(arr[x]));
			ts.add(Integer.parseInt(arr[x]));
		}

		System.out.println(ts);
	}
}

十、Map集合概述

Map集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。
1,添加。
put(K key, V value) 
putAll(Map<? extends K,? extends V> m) 
2,删除。
clear() 
remove(Object key) 
3,判断。
containsValue(Object value) 
containsKey(Object key) 
isEmpty() 
4,获取。
get(Object key) 
size() 
values() 
entrySet() 
keySet() 
Map
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。
|--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。
和Set很像。
其实大家,Set底层就是使用了Map集合。
示例代码10:

import java.util.*;
class  MapDemo
{
	public static void main(String[] args) 
	{
		Map<String,String> map = new HashMap<String,String>();


		//添加元素,添加元素,如果出现添加时,相同的键。那么后添加的值会覆盖原有键对应值。
		//并put方法会返回被覆盖的值。
		System.out.println("put:"+map.put("01","zhangsan1"));
		System.out.println("put:"+map.put("01","wnagwu"));
		map.put("02","zhangsan2");
		map.put("03","zhangsan3");

		System.out.println("containsKey:"+map.containsKey("022"));
		//System.out.println("remove:"+map.remove("02"));
		System.out.println("get:"+map.get("023"));
		map.put("04",null);
		System.out.println("get:"+map.get("04"));
		//可以通过get方法的返回值来判断一个键是否存在。通过返回null来判断。
		
		//获取map集合中所有的值。
		Collection<String> coll = map.values();
		System.out.println(coll);
		System.out.println(map);

	}
}

map集合的两种取出方式:
1,Set<k> keySet:将map中所有的键存入到Set集合。因为set具备迭代器。
所有可以迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。
Map集合的取出原理:将map集合转成set集合。在通过迭代器取出。
2,Set<Map.Entry<k,v>> entrySet:将map集合中的映射关系存入到了set集合中,

而这个关系的数据类型就是:Map.Entry

Entry其实就是Map中的一个static内部接口。
为什么要定义在内部呢?
因为只有有了Map集合,有了键值对,才会有键值的映射关系。
关系属于Map集合中的一个内部事物。
而且该事物在直接访问Map集合中的元素。

示例代码11:

import java.util.*;
class MapDemo2 
{
	public static void main(String[] args) 
	{
		Map<String,String> map = new HashMap<String,String>();

		map.put("02","zhangsan2");
		map.put("03","zhangsan3");
		map.put("01","zhangsan1");
		map.put("04","zhangsan4");

		//第一种取数据方式:将Map集合中的映射关系取出。存入到Set集合中。
		Set<Map.Entry<String,String>> entrySet = map.entrySet();
		Iterator<Map.Entry<String,String>> it = entrySet.iterator();
		while(it.hasNext())
		{
			Map.Entry<String,String> me = it.next();
			String key = me.getKey();
			String value = me.getValue();
			System.out.println(key+":"+value);
		}

		
		//第二种取数据方式:先获取map集合的所有键的Set集合,keySet()。
		Set<String> keySet = map.keySet();

		//有了Set集合。就可以获取其迭代器。
		Iterator<String> it = keySet.iterator();
		while(it.hasNext())
		{
			String key = it.next();
			//有了键可以通过map集合的get方法获取其对应的值。
			String value  = map.get(key);
			System.out.println("key:"+key+",value:"+value);
		}


	}
}

Map.Entry内部实现:

Map.Entry 其实Entry也是一个接口,它是Map接口中的一个内部接口。

interface Map
{
public static interface Entry
{
public abstract Object getKey();
public abstract Object getValue();
}
}

class HashMap implements Map
{
class Hahs implements Map.Entry
{
public  Object getKey(){}
public  Object getValue(){}
}

}

十一、HashMap集合

示例代码12:

/*
map集合被使用是因为具备映射关系。
"yureban"   Student("01" "zhangsan");
"yureban" Student("02" "lisi");

"jiuyeban" "01" "wangwu";
"jiuyeban" "02" "zhaoliu";

一个学校有多个教室。每一个教室都有名称。
 */
import java.util.*;

class Student {
	private String id;
	private String name;

	Student(String id, String name) {
		this.id = id;
		this.name = name;
	}

	public String toString() {
		return id + ":::" + name;
	}
}

class MapDemo3 {

	public static void demo() {
		HashMap<String, List<Student>> czbk = new HashMap<String, List<Student>>();

		List<Student> reyu = new ArrayList<Student>();
		List<Student> jiuye = new ArrayList<Student>();

		czbk.put("yureban", reyu);
		czbk.put("jiuyeban", jiuye);

		reyu.add(new Student("01", "zhagnsa"));
		reyu.add(new Student("04", "wangwu"));
		jiuye.add(new Student("01", "zhouqi"));
		jiuye.add(new Student("02", "zhaoli"));

		Iterator<String> it = czbk.keySet().iterator();

		while (it.hasNext()) {
			String roomName = it.next();
			List<Student> room = czbk.get(roomName);

			System.out.println(roomName);
			getInfos(room);
		}

	}

	public static void getInfos(List<Student> list) {
		Iterator<Student> it = list.iterator();
		while (it.hasNext()) {
			Student s = it.next();
			System.out.println(s);
		}
	}

	public static void main(String[] args) {
		demo();
	}

}


十二、TreeMap集合

TreeMap:对键进行排序,排序原理与TreeSet相同。

练习:
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。
希望打印结果:a(1)c(2).....
通过结果发现,每一个字母都有对应的次数。
说明字母和次数之间都有映射关系。
注意了,当发现有映射关系时,可以选择map集合。
因为map集合中存放就是映射关系。
什么使用map集合呢?
当数据之间存在这映射关系时,就要先想map集合。
思路:
1,将字符串转换成字符数组。因为要对每一个字母进行操作。
2,定义一个map集合,因为打印结果的字母有顺序,所以使用treemap集合。
3,遍历字符数组。
将每一个字母作为键去查map集合。
如果返回null,将该字母和1存入到map集合中。
如果返回不是null,说明该字母在map集合已经存在并有对应次数。
那么就获取该次数并进行自增。,然后将该字母和自增后的次数存入到map集合中。覆盖调用原理键所对应的值。
4,将map集合中的数据变成指定的字符串形式返回。

示例代码13:

import java.util.*;
class  MapTest3
{
	public static void main(String[] args) 
	{
		String s= charCount("ak+abAf1c,dCkaAbc-defa");
		System.out.println(s);
	}
	
	public static String charCount(String str)
	{
		char[] chs = str.toCharArray();
		TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
		int count = 0;
		for(int x=0; x<chs.length; x++)
		{
			if(!(chs[x]>='a' && chs[x]<='z' || chs[x]>='A' && chs[x]<='Z'))
				continue;
			Integer value = tm.get(chs[x]);
			if(value!=null)
				count = value;
			count++;
			tm.put(chs[x],count);//直接往集合中存储字符和数字,为什么可以,因为自动装箱。
			count = 0;
			/*
			if(value==null)
			{
				tm.put(chs[x],1);
			}
			else
			{
				value = value + 1;
				tm.put(chs[x],value);
			}
			*/

		}


		//System.out.println(tm);


		StringBuilder sb = new StringBuilder();


		Set<Map.Entry<Character,Integer>> entrySet = tm.entrySet();
		Iterator<Map.Entry<Character,Integer>>  it = entrySet.iterator();


		while(it.hasNext())
		{
			Map.Entry<Character,Integer> me = it.next();
			Character ch = me.getKey();
			Integer value = me.getValue();
			sb.append(ch+"("+value+")");
		}


		return sb.toString();
	}


}


                                     ------------android培训java培训、期待与您交流! --------------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值