Java集合框架
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java集合框架,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含20935字,纯文字阅读大概需要30分钟。
内容图文
![Java集合框架](/upload/InfoBanner/zyjiaocheng/598/8404989974914054a5651d13c51706b0.jpg)
集合框架
概念:对象的容器,定义了对多个对象进行操作的常用方法
和数组的区别:
- 数组长度固定,集合长度不固定
- 数组可以存储基本类型和引用类型,集合只能存储引用类型
Collection
Collection父接口
特点:代表一组任意类型的对象,无序、无下表、不能重复。
Collection使用
public class Demo1 {
public static void main(String[] args) {
//创建集合
Collection collection = new ArrayList();
//1.添加元素
collection.add("苹果");
collection.add("西瓜");
collection.add("榴莲");
System.out.println("元素个数:"+collection.size());
System.out.println(collection);
//2.删除元素
collection.remove("榴莲");
collection.clear();
System.out.println("删除之后:"+collection.size());
//3.遍历元素
for (Object object : collection) { //增强for
System.out.println(object);
}
Iterator it = collection.iterator();//迭代器
while(it.hasNext()) {
String s = (String)it.next();
System.out.println(s);
//it.remove();
}
//迭代过程中不能使用其他方法来改变迭代元素(并发修改异常)
//4.判断
System.out.println(collection.contains("西瓜"));//true
System.out.println(collection.isEmpty());//false
}
}
public class Student {
private String name;
private int age;
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 + "]";
}
@Override
public boolean equals(Object obj) {
if(this==obj){
return true;
}
if(obj==null)
return false;
if(obj instanceof Student){
Student s = (Student)obj;
if(this.name.equals(s.getName())&&this.age==s.getAge())
return true;
}
return false;
}
}
public class Demo2 {
public static void main(String[] args) {
Collection collection = new ArrayList();
Student s1 = new Student("张三",20);
Student s2 = new Student("张无忌",18);
Student s3 = new Student("王二",22);
//1.添加数据
collection.add(s1);
collection.add(s2);
collection.add(s3);
System.out.println("元素个数:"+collection.size());
System.out.println(collection.toString());
//2.删除
collection.remove(s1);
collection.clear();//只是删除了地址,对象依然存在
System.out.println("删除之后:"+collection.size());
//3.遍历
for(Object object : collection) {
Student s = (Student)object;
System.out.println(s.toString());
}
Iterator it = collection.iterator();
while(it.hasNext()) {
Student s = (Student)it.next();
System.out.println(s.toString());
}
//4.判断
System.out.println(collection.contains(s1));//true
}
}
List
List子接口
特点:有序、有下标、元素可以重复
List使用
public class Demo3 {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("苹果");
list.add("小米");
list.add(0,"华为");
System.out.println("元素个数:"+list.size());
System.out.println(list.toString()); [华为,苹果,小米]
//list.remove("苹果");
list.remove(0);
System.out.println("删除之后:"+list.size());
System.out.println(list.toString()); [苹果,小米]
//遍历
for(int i = 0; i < list.size; i++) {
System.out.println(list.get(i));
}
//增强for
for(Object object : list) {
System.out.println(object);
}
//迭代器
Iterator it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//列表迭代器 可以向前向后遍历,添加、删除、修改元素
ListIterator lit = list.listIterator();
while(lit.hasNext()) {
System.out.println(lit.nextIndex()+":"+lit.next());
}
while(lit.hasPrevious()) {
System.out.println(lit.previousIndex()+":"+lit.previous());
}
//判断
System.out.println(list.contains("苹果"));//true
System.out.println(list.isEmpty());//false
//获取位置
System.out.println(list.indexOf("华为"));
}
}
public class Demo4 {
public static void main(String[] args) {
List list = new ArrayList();
//自动装箱
list.add(20);
list.add(30);
list.add(40);
list.add(50);
list.add(60);
System.out.println("元素个数:"+list.size());
System.out.println(list.toString());
list.remove(0);//list.remove((Object)20); list.remove(new Integer(20));
System.out.println("删除之后:"+list.size());
System.out.println(list.toString());
//subList
List subList = list.subList(1,3);
System.out.println(subList.toString()); //[40,50]
}
}
List实现类
- ArrayList
- 数组结构实现,查询快、增删慢
- 运行效率快、线程不安全
- Vector
- 数组结构实现,查询快、增删慢
- 运行效率慢、线程安全
- LinkedList
- 链表结构实现,增删快,查询慢
ArrayList使用
public class Demo5 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList<>();
Student s1 = new Student("刘德华",20);
Student s1 = new Student("郭富城",22);
Student s1 = new Student("梁朝伟",18);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
System.out.println("元素个数:"+arrayList.size());
System.out.println(arrayList.toString());
arrayList.remove(s1);
arrayList.remove(new Student("刘德华",20));//equals(this==obj) 重写
System.out.println("删除之后:"+arrayList.size());
//迭代器遍历
Iterator it = arrayList.iterator();
while(it.hasNext()){
Student s = (Student)it.next();
System.out.println(s.toString());
}
ListIterator lit = arrayList.listIterator();
while(lit.hasNext()){
Student s = (Student)lit.next();
System.out.println(s.toString());
}
while(lit.hasPrevious()){
Student s = (Student)lit.previous();
System.out.println(s.toString());
}
//判断
System.out.println(arrayList.contains(s1)); //true
System.out.println(arrayList.contains(new Student("梁朝伟",18))); //true
System.out.println(arrayList.isEmpty());//false
//查找
System.out.println(arrayList.indexOf(s1));
}
}
源码分析
- 默认容量DEFAULT_CAPACITY = 10
- 如果没有向集合中添加任何元素,容量为0;添加一个元素之后,变为10。添加第11个,容量变为5,每次扩容大小为原来的1.5倍
- elementData存放元素的数组
- size实际元素个数
- add()
public boolean add(E e) {
ensureCapacityInternal(size+1);
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
minCapacity = Math.max(DEFAULT_CAPACITY,minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if(minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //0
int newCapacity = oldCapacity + (oldCapacity >> 1);//0
if(newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if(newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData,newCapacity);
}
Vector
public class Demo1 {
public static void main(String[] args) {
Vector vector = new Vector<>();
vector.add("草莓");
vector.add("芒果");
vector.add("西瓜");
System.out.println("元素个数:"+vector.size());
//删除
vector.remove(0);
vector.remove("西瓜");
vector.clear();
//遍历 枚举器
Enumeration en = vector.elements();
while(en.hasMoreElements()){
String o = (String)en.nextElement();
System.out.println(o);
}
//判断
System.out.println(vector.contains("西瓜"));//true
System.out.println(vector.isEmpty());//false
//firstElement、lastElement、elementAt();
}
}
LinkedList使用
//双向链表
public class Demo2 {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList<>();
Student s1 = new Student("刘德华",20);
Student s1 = new Student("郭富城",22);
Student s1 = new Student("梁朝伟",18);
//添加
linkedList.add(s1);
linkedList.add(s2);
linkedList.add(s3);
System.out.println("元素个数:"+linkedList.size());
System.out.println(linkedList.toString());
//删除
linkedList.remove(s1);
System.out.println("删除之后:"+linkedList.size());
linkedList.clear();
//for遍历
for(int i = 0; i < linkedList.size(); i++){
System.out.println(linkedList.get(i));
}
//增强for遍历
for(Object object : linkedList) {
Student s = (Student)object;
System.out.println(s.toString());
}
//迭代器
Iterator it = linkedList.iterator();
while(it.hasNext()){
Student s = (Student)it.next();
System.out.println(s.toString());
}
//ListIterator
//判断
System.out.println(linkedList.contains(s1));
System.out.println(linkedList.isEmpty());
System.out.println(linkedList.indexOf(s1));
}
}
LinkedList源码分析
transient int size = 0; //集合大小
transient Node<E> first; //头节点
transient Node<E> last; //尾节点
public boolean add(E e){
linkLast(e);
return true;
}
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next){
this.item = element;
this.next = next;
this.prev = prev;
}
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if(l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
ArrayList和LinkedList区别
泛型
Java泛型本质是参数化类型,把类型作为参数传递
常见形式有泛型类、泛型接口、泛型方法
好处:
-
提高代码的重用性
-
防止类型转换异常,提高代码的安全性
泛型类
//泛型类 T是类型占位符
public class MyGeneric<T> {
//使用泛型T
//1.创建变量(但不能实例化)
T t;
//2.作为方法的参数
public void show(T t) {
System.out.println(t);
}
//3.使用泛型作为方法的返回值
public T getT(){
return t;
}
}
public class TestGeneric {
public static void main(String[] args) {
MyGeneric<String> myGeneric = new MyGeneric<>();
myGeneric.t = "hello";
myGeneric.show("大家好,加油");
String string = myGeneric.getT();
MyGeneric<Integer> myGeneric2 = new MyGeneric<Integer>();
myGeneric2.t = 100;
myGeneric2.show(200);
Integer integer = myGeneric2.getT();
}
}
注意:泛型只能是引用类型,不同泛型对象不能相互赋值
泛型接口
方式一
public interface MyInterface<T> {
String name = "张三";//静态常量,默认有public static final修饰
//不能创建泛型静态常量
T server(T t);
}
public class MyInterfaceImpl implements MyInterface<String> {
@Override
public String server(String t) {
System.out.println(t);
return t;
}
}
public class TestGeneric {
public static void main(String[] args) {
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.server("xxxxxxx");
}
}
方式二
public class MyInterfaceImpl2<T> implements MyInterface<T> {
@Override
public T server(T t) {
System.out.println(t);
return t;
}
}
public class TestGeneric {
public static void main(String[] args) {
MyInterfaceImpl2<Integer> impl2 = new MyInterfaceImpl2<>();
impl2.server(1000);
}
}
泛型方法
public class MyGenericMethod {
//泛型方法
public <T> T show(T t) {
System.out.println("泛型方法"+t);
return t;
}
}
public class TestGeneric {
public static void main(String[] args) {
MyGenericMethod myGenericMethod = new MyGenericMethod();
myGenericMethod.show("中国加油");
myGenericMethod.show(200);
myGenericMethod.show(3.14);
}
}
泛型集合
参数化类型、类型安全的集合,强制集合元素的类型必须一致
特点:
- 编译时即可检查,而非运行时抛出异常
- 访问时,不必类型转换(拆箱)
- 不同泛型之间引用不能相互赋值,泛型不存在多态
public class Demo3 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("xxx");
arrayList.add("yyy");
//arrayList.add(10);
//for(Object object : arrayList) {
// String str = (String)object;
// System.out.println(str);
//}
for(String string : arrayList){
System.out.println(string);
}
ArrayList<Student> arrayList2 = new ArrayList<Student>();
Student s1 = new Student("刘德华",20);
Student s1 = new Student("郭富城",22);
Student s1 = new Student("梁朝伟",18);
arrayList2.add(s1);
arrayList2.add(s2);
arrayList2.add(s3);
//arrayList2.add("xxx");
Iterator<Student> it = arrayList2.iterator();
while(it.hasNext()) {
Student s = it.next();
System.out.println(s.toString());
}
}
}
Set集合
- 特点:无序、无下标、元素不可重复
- 方法:全部继承自Collection中的方法
Set使用
public class Demo1 {
public static void main(String[] args) {
Set<String> set = new HashSet<>(){
//1.添加数据
set.add("苹果");
set.add("华为");
set.add("小米");
set.add("华为");
System.out.println("数据个数:"+set.size());
System.out.println(set.toString());
//2.删除数据
set.remove("小米");
System.out.println(set.toString());
//3.遍历(重点)
//增强for
for(String string : set) {
System.out.println(string);
}
//迭代器
Iterator<String> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//4.判断
System.out.println(set.contains("华为"));//true
System.out.println(set.isEmpty());//false
}
}
}
HashSet使用
存储结构:哈希表(数组+链表+红黑树)
public class Demo2 {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
//1.添加元素
hashSet.add("刘德华");
hashSet.add("梁朝伟");
hashSet.add("林志玲");
hashSet.add("周润发");
System.out.println("元素个数:"+hashSet.size());
System.out.println(hashSet.toString());
//2.删除数据
hashSet.remove("刘德华");
//3.遍历
//增强for 迭代器
//4.判断
}
}
HashSet存储方式
根据hashcode计算保存位置,如果此位置为空就保存,否则再执行equals方法,如果其为true,则认为是重复,否则形成链表
HashSet<Person> persons = new HashSet<>();
Person p1 = new Person("梁朝伟",25);
persons.add(p1);
persons.add(new Person("梁朝伟",25));//可以加进去 this==equals
//重写hashcode
public class Person {
private String name;
private int age;
public Person(){
}
public Person(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 "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
int n1 = this.name.hashCode();
int n2 = this.age;
return n1+n2;
}
@Override
public boolean equals(Object obj) {
if(this==obj) {
return true;
}
if(obj==null) {
return false;
}
if(obj instanceof Person) {
Person p = (Person)obj;
if(this.name.equals(p.getName())&&this.age==p.getAge())
return true;
}
return false;
}
}
注意:只重写hashCode会形成链表
重写hashCode用final int prime = 31的原因
- 31是质数,可以减少散列冲突
- 提高执行效率 31*i=(i<<5)-i
TreeSet使用(红黑树)
- 基于排列顺序实现元素不重复
- 实现了SortedSet接口,对集合元素自动排序
- 元素对象的类型必须实现Comparable接口,指定排序规则
- 通过CompareTo方法确定是否为重复元素(返回值为0)
public class Demo4 {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>();
//1.删除
treeSet.add("xyz");
treeSet.add("abc");
treeSet.add("hello");
System.out.println("元素个数:"+treeSet.size());
System.out.println(treeSet.toString());
//2.删除
//3.遍历 增强for 迭代器 4.判断
}
}
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(){
}
public Person(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 "Person [name=" + name + ", age=" + age + "]";
}
//先按姓名比,再按年龄比
@Override
public int compareTo(Person o) {
int n1 = this.getName().compareTo(o.getName());
int n2 = this.age-o.getAge();
return n1==0?n2:n1;
}
}
public class Demo5 {
public static void main(String[] args) {
TreeSet<Person> persons = new TreeSet<>();
Person p1 = new Person("xyz",20);
Person p2 = new Person("hello",22);
Person p3 = new Person("zhangsan",25);
Person p4 = new Person("zhangsan",20);
//1.添加
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
System.out.println("元素个数:"+persons.size());
System.out.println(persons.toString());
//2.删除
persons.remove(new Person("zhangsan",20));
//3.遍历
}
}
Comparator:实现定制比较(比较器)
public class Demo6 {
public static void main(String[] args) {
//创建集合,并指定比较规则
TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>(){
@Override
public int compare(Person o1, Person o2) {
int n1 = o1.getAge()-o2.getAge();
int n2 = o1.getName()-o2.getName();
return n1==0?n2:n1;
}
});
}
}
接口不能实例化,这里是创建了一个匿名内部类来实现接口的
TreeSet实例
使用TreeSet集合实现字符串按照长度进行排序
public class Demo7 {
public static void main(String[] args) {
//创建集合并指定比较规则
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
int n1 = o1.length()-o2.length();
int n2 = o1.compareTo(o2);
return n1==0?n2:n1;
}
});
//添加数据
treeSet.add("helloworld");
treeSet.add("pingguo");
treeSet.add("lisi");
treeSet.add("zhangsan");
treeSet.add("beijing");
}
}
Map集合
特点
- 用于存储任意键值对(Key-Value)
- 键:无序、无下标、不允许重复
- 值:无序、无下标、允许重复
Map父接口
public class Demo1 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
//1.添加
map.put("cn","中国");
map.put("uk","英国");
map.put("usa","美国");
map.put("cn","zhongguo");//替换
System.out.println("元素个数:"+map.size());
System.out.println(map.toString());
//2.删除
map.remove("usa");
//3.遍历
//使用keySet();
Set<String> keyset = map.keySet();
for (String key : keyset) {
System.out.println(key+"-----"+map.get(key));
}
//使用entrySet();
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey()+"-----"+entry.getValue());
}
//4.判断
System.out.println(map.containsKey("cn"));
System.out.println(map.containsValue("秦国"));
}
}
HashMap(重点)
线程不安全,运行效率快;允许使用null作为key或是value
存储结构:哈希表(数组+链表+红黑树)
使用key的hashcode和equals作为重复判定
public class Demo2 {
public static void main(String[] args) {
//创建集合
HashMap<Student, String> students = new HashMap<Student, String>();
//1.添加元素
Student s1 = new Student("孙悟空",100);
Student s2 = new Student("猪八戒",101);
Student s3 = new Student("沙和尚",102);
students.put(s1, "北京");
students.put(s2, "上海");
students.put(s3, "杭州");
students.put(new Student("沙和尚",102),"杭州");
System.out.println("元素个数:"+students.size());
System.out.println(students.toString());
//2.删除
students.remove(s1);
System.out.println("删除之后:"+students.size());
//3.遍历 使用keySet();
for (Student key : students.keySet()) {
System.out.println(key.toString()+"====="+students.get(key));
}
//使用entrySet();
for (Map.Entry<Student, String> entry : students.entrySet()) {
System.out.println(entry.getKey()+"====="+entry.getValue());
}
//判断
System.out.println(students.containsKey(s1));
System.out.println(students.containsValue("杭州"));
}
}
public class Student {
private String name;
private int stuNo;
public Student() {
}
public Student(String name, int stuNo) {
super();
this.name = name;
this.stuNo = stuNo;
}
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStuNo(){
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
@Override
public String toString() {
return "Student [name=" + name + ", stuNo=" + stuNo + "]";
}
}
HashMap源码分析
static final int DEFAULT_CAPACITY = 1<<4;
static final int MAXIMUM_CAPACITY = 1<<30;
static final float DEFAULT_LOAD_FACTOR = 0.75f; //加载因子
static final int TREEIFY_THRESHOLD = 8; //链表长度大于8,并且数组长度大于64,就会把链表变为红黑树(查找效率高)
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
transient Node<K,V>[] table;//哈希表中的数组
transient int size;
//刚创建hashmap之后没有添加元素table=null size=0 目的节省空间
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V>[] p; int n,i;
if ((tab = table) ==null) || (n = tab.length) == 0)
n = (tab = resize()).length;
}
总结
1.HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量调整为16
2.当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍,目的是减少调整元素的个数
3.jdk1.8 当每个链表长度大于8,并且数组元素个数大于等于64时,会调整为红黑树,目的提高执行效率
4.jdk1.8 当链表长度小于6时,调整成链表
5.jdk1.8以前,链表是头插入,jdk1.8以后是尾插入
HashSet使用HashMap的Key来保存数据,使用同一个结构
HashMap
- 线程不安全,运行效率快;允许用null作为key或是value
Hashtable
- 线程安全,运行效率慢;不允许null作为key或是value
Properties
- Hashtable的子类,要求key和value都是String,通常用于配置文件的读取
TreeMap的使用
实现了SortedMap接口,可以对key自动排序
类比TreeSet
public class Demo3 {
public static void main(String[] args) {
TreeMap<Student, String> treeMap = new TreeMap<Student, String>();
//添加
Student s1 = new Student("孙悟空",100);
Student s2 = new Student("猪八戒",101);
Student s3 = new Student("沙和尚",102);
treeMap.put(s1, "北京");
treeMap.put(s2, "上海");
treeMap.put(s3, "深圳");
System.out.println("元素个数:"+treeMap.size());
System.out.println(treeMap.toString());
//删除
treeMap.remove(s3);
//keySet entrySet
}
}
TreeSet使用TreeMap的Key来保存数据
Collections工具类
集合工具类,定义了除了存取以外的集合常用方法
public class Demo4 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(20);
list.add(5);
list.add(12);
list.add(30);
list.add(6);
//sort排序
System.out.println("排序之前:"+list.toString());
Collections.sort(list);
System.out.println("排序之后:"+list.toString());
//binarySearch二分查找
int i = Collections.binarySearch(list, 12);
System.out.println(i);
//copy复制
List<Integer> dest = new ArrayList<>();
for (int k = 0; k < list.size(); k++) {
dest.add(0);
}
Collections.copy(dest,list);
System.out.println(dest.toString());
//reverse反转
Collections.reverse(list);
System.out.println("反转之后:"+list);
//shuffle 打乱
Collections.shuffle(list);
System.out.println("打乱之后:"+list);
//list转成数组
Integer[] arr = list.toArray(new Integer[10]);
//数组变成集合
String[] names = {"张三","李四","王五"};
List<String> list2 = Arrays.asList(names);
//受限集合,不能添加和删除
System.out.println(list2);
//把基本类型转成集合时,需要修改为包装类
Integer[] nums = {100,200,300,400,500};
List<Integer> list3 = Arrays.asList(nums);
System.out.println(list3);
}
}
内容总结
以上是互联网集市为您收集整理的Java集合框架全部内容,希望文章能够帮你解决Java集合框架所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。