集合
Collect接口
- Collection集合,首先是一个接口。是Java中所有集合的总接口。一个 Collection接口代表一组 Object类,即 Collection接口的元素。
- Java不提供直接继承自Collection接口的类,只提供继承于的子接口(如List接口和set接口)。
- Collection接口存储一组不唯一,无序的对象。
List 接口
- List接口是一个有序的Collection接口,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
- List 接口存储一组不唯一,有序(插入顺序)的对象。
实现类
ArrayList
ArrayList集合的底层逻辑是数组。该类也是实现了List接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。
代码
package com.collection; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Collection { public static void main(String[] args) { //ArrayList
普通的可变长的数组 元素有序,可重复 List arrayList = new ArrayList(); arrayList.add(true); arrayList.add(123); arrayList.add(123.456); arrayList.add("String"); arrayList.add(true); arrayList.add(123); arrayList.add(123.456); arrayList.add("String"); //集合直接打印 System.out.print("----ArrayList集合直接打印----" + "\n" + arrayList + "\t"); //普通for循环打印 System.out.println("\n----ArrayList集合普通for循环打印----"); for (int i = 0; i < arrayList.size(); i++) { System.out.print(arrayList.get(i)+"\t"); } //迭代器打印 Iterator iterator = arrayList.iterator(); System.out.println("\n----ArrayList集合迭代器打印----"); while (iterator.hasNext()) { System.out.print(iterator.next() + "\t"); } //for-each循环打印 System.out.println("\n----ArrayList集合for-each循环打印----"); for (Object i : arrayList) { System.out.print(i+"\t"); } /* ----ArrayList集合直接打印---- [true, 123, 123.456, String, true, 123, 123.456, String] ----ArrayList集合普通for循环打印---- true 123 123.456 String true 123 123.456 String ----ArrayList集合迭代器打印---- true 123 123.456 String true 123 123.456 String ----ArrayList集合for-each循环打印---- true 123 123.456 String true 123 123.456 String */ } } 方法 描述 add() 将元素插入到指定位置的 arraylist 中 addAll() 添加集合中的所有元素到 arraylist 中 clear() 删除 arraylist 中的所有元素 clone() 复制一份 arraylist contains() 判断元素是否在 arraylist get() 通过索引值获取 arraylist 中的元素 indexOf() 返回 arraylist 中元素的索引值 removeAll() 删除存在于指定集合中的 arraylist 里的所有元素 remove() 删除 arraylist 里的单个元素 LinkedList
- 该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,LinkedList 查找效率低。
该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。例如:
List list=Collections.synchronizedList(newLinkedList(...));
代码
package com.collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class Collection { public static void main(String[] args) { //LinkedList
底层为双向链表的集合结构 ,元素有序,可重复 List linkedList = new LinkedList(); linkedList.add(true); linkedList.add(123); linkedList.add(123.456); linkedList.add("String"); linkedList.add(true); linkedList.add(123); linkedList.add(123.456); linkedList.add("String"); //集合直接打印 System.out.print("----linkedList集合直接打印----" + "\n" + linkedList + "\t"); //普通for循环打印 System.out.println("\n----linkedList普通for循环打印----"); for (int i = 0; i < linkedList.size(); i++) { System.out.print(linkedList.get(i) + "\t"); } //迭代器打印 Iterator iterator = linkedList.iterator(); System.out.println("\n----linkedList集合迭代器打印----"); while (iterator.hasNext()) { System.out.print(iterator.next() + "\t"); } //for-each循环打印 System.out.println("\n----linkedList集合for-each循环打印----"); for (Object i : linkedList) { System.out.print(i + "\t"); } /* ----linkedList集合直接打印---- [true, 123, 123.456, String, true, 123, 123.456, String] ----linkedList普通for循环打印---- true 123 123.456 String true 123 123.456 String ----linkedList集合迭代器打印---- true 123 123.456 String true 123 123.456 String ----linkedList集合for-each循环打印---- true 123 123.456 String true 123 123.456 String */ } } 方法 描述 public boolean add(E e) 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 public void add(int index, E element) 向指定位置插入元素。 public boolean addAll(Collection c) 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。 public boolean addAll(int index, Collection c) 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。 public void addFirst(E e) 元素添加到头部。 public void addLast(E e) 元素添加到尾部。 public boolean offer(E e) 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 public boolean offerFirst(E e) 头部插入元素,返回是否成功,成功为 true,失败为 false。 public boolean offerLast(E e) 尾部插入元素,返回是否成功,成功为 true,失败为 false。 public void clear() 清空链表。 public E removeFirst() 删除并返回第一个元素。 public E removeLast() 删除并返回最后一个元素。 public boolean remove(Object o) 删除某一元素,返回是否成功,成功为 true,失败为 false。 public E remove(int index) 删除指定位置的元素。 public E poll() 删除并返回第一个元素。 public E remove() 删除并返回第一个元素。 public boolean contains(Object o) 判断是否含有某一元素。 public E get(int index) 返回指定位置的元素。 public E getFirst() 返回第一个元素。 public E getLast() 返回最后一个元素。 public int indexOf(Object o) 查找指定元素从前往后第一次出现的索引。 public int lastIndexOf(Object o) 查找指定元素最后一次出现的索引。 public E peek() 返回第一个元素。 public E element() 返回第一个元素。 public E peekFirst() 返回头部元素。 public E peekLast() 返回尾部元素。 public E set(int index, E element) 设置指定位置的元素。 public Object clone() 克隆该列表。 public Iterator descendingIterator() 返回倒序迭代器。 public int size() 返回链表元素个数。 public ListIterator listIterator(int index) 返回从指定位置开始到末尾的迭代器。 public Object[] toArray() 返回一个由链表元素组成的数组。 public T[] toArray(T[] a) 返回一个由链表元素转换类型而成的数组。 Vector
该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。
代码
package com.collection; import java.util.Iterator; import java.util.List; import java.util.Vector; public class Collection { public static void main(String[] args) { //vector
普通的可变长的数组 元素有序,可重复,允许多线程 List vector = new Vector(); vector.add(true); vector.add(123); vector.add(123.456); vector.add("String"); vector.add(true); vector.add(123); vector.add(123.456); vector.add("String"); //集合直接打印 System.out.print("----vector集合直接打印----" + "\n" + vector + "\t"); //普通for循环打印 System.out.println("\n----vector集合普通for循环打印----"); for (int i = 0; i < vector.size(); i++) { System.out.print(vector.get(i)); } //迭代器打印 Iterator iterator = vector.iterator(); System.out.println("\n----vector集合迭代器打印----"); while (iterator.hasNext()) { System.out.print(iterator.next() + "\t"); } //for-each循环打印 System.out.println("\n----vector集合for-each循环打印----"); for (Object i : vector) { System.out.print(i + "\t"); } /* ----vector集合直接打印---- [true, 123, 123.456, String, true, 123, 123.456, String] ----vector集合普通for循环打印---- true123123.456Stringtrue123123.456String ----vector集合迭代器打印---- true 123 123.456 String true 123 123.456 String ----vector集合for-each循环打印---- true 123 123.456 String true 123 123.456 String */ } }
Set接口
- Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。
- Set 接口存储一组唯一,无序的对象。
- Set没有带索引的方法,因此不能使用普通for循环
实现类
HashSet
HashSet类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。
代码
package com.collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Collection {
public static void main(String[] args) {
//HashSet 底层存储数据的结构是一个哈希表 元素无序,不可重复
Set hashSet = new HashSet();
hashSet.add(true);
hashSet.add(123);
hashSet.add(123.456);
hashSet.add("String");
hashSet.add(true);
hashSet.add(123);
hashSet.add(123.456);
hashSet.add("String");
//集合直接打印
System.out.print("----HashSet集合直接打印----" + "\n" + hashSet + "\t");
//Set没有带索引的方法,因此不能使用普通for循环
//迭代器打印
System.out.println("\n----HashSet集合迭代器打印----");
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + "\t");
}
//for-each循环打印
System.out.println("\n----HashSet集合for-each循环打印----");
for (Object i : hashSet) {
System.out.print(i + "\t");
}
/*
----HashSet集合直接打印----
[123.456, 123, String, true]
----HashSet集合迭代器打印----
123.456 123 String true
----HashSet集合for-each循环打印----
123.456 123 String true
*/
}
}
LinkedHashSet
- 哈希表和链表实现的Set接口,具有可预测的迭代顺序。
- 由链表保证存入和取出的顺序一致
由哈希表保证存入的元素不允许重复
代码
package com.collection; import java.util.LinkedHashSet; import java.util.Iterator; public class Collection { public static void main(String[] args) { //linkedHashSet
底层存储数据的结构是哈希表和链表 链表保证元素有序,哈希表保证元素唯一 LinkedHashSet linkedHashSet = new LinkedHashSet(); linkedHashSet.add(true); linkedHashSet.add(123); linkedHashSet.add(123.456); linkedHashSet.add("String"); linkedHashSet.add(true); linkedHashSet.add(123); linkedHashSet.add(123.456); linkedHashSet.add("String"); //集合直接打印 System.out.print("----linkedHashSet集合直接打印----" + "\n" + linkedHashSet + "\t"); //Set没有带索引的方法,因此不能使用普通for循环 //迭代器打印 System.out.println("\n----linkedHashSet集合迭代器打印----"); Iterator iterator = linkedHashSet.iterator(); while (iterator.hasNext()) { System.out.print(iterator.next() + "\t"); } //for-each循环打印 System.out.println("\n----linkedHashSet集合for-each循环打印----"); for (Object i : linkedHashSet) { System.out.print(i + "\t"); } /* ----linkedHashSet集合直接打印---- [true, 123, 123.456, String] ----linkedHashSet集合迭代器打印---- true 123 123.456 String ----linkedHashSet集合for-each循环打印---- true 123 123.456 String */ } } 哈希值
- 哈希值是JDK根据对象的地址或字符串或数字算出来的int类型的一个数字值
Object类中有一个方法可以获得对象的哈希值 --- public int hashCode(T o)
代码
package com.collection;
public class HashCode {
public static void main(String[] args) {
String s = "String";
System.out.println("字符串s的哈希值1:" + s.hashCode());
System.out.println("字符串s的哈希值2:" + s.hashCode());
/*
字符串s的哈希值1:-1808118735
字符串s的哈希值2:-1808118735
*/
Person p = new Person("AAA", 20);
System.out.println("人员类对象p的哈希值1:" + p.hashCode());
System.out.println("人员类对象p的哈希值2:" + p.hashCode());
/*
人员类对象p的哈希值1:460141958
人员类对象p的哈希值2:460141958
*/
//结论:默认情况下同一对象不用次调用hashCode()方法获得的哈希值是相同的
System.out.println("----------------------------");
String s1 = "String";
String s2 = "String";
System.out.println("字符串s1的哈希值:" + s1.hashCode());
System.out.println("字符串s2的哈希值:" + s2.hashCode());
/*
字符串s1的哈希值:-1808118735
字符串s2的哈希值:-1808118735
*/
//结论:默认情况下相同数据类型相同值,调用hashCode()方法获得的哈希值是相同的
Person p1 = new Person("AAA", 20);
Person p2 = new Person("AAA", 20);
System.out.println("人员类对象p1的哈希值:" + p1.hashCode());
System.out.println("人员类对象p2的哈希值:" + p2.hashCode());
/*
人员类对象p1的哈希值:1163157884
人员类对象p2的哈希值:1956725890
*/
//结论:默认情况下不同自定义对象相同值,调用hashCode()方法获得的哈希值是不同的
//特殊情况
System.out.println("重地".hashCode());
System.out.println("通话".hashCode());
//二者哈希值相同
//1179395
//1179395
}
}
TreeSet
该类实现了Set接口,由哈希表支持,可以实现排序等功能。
代码
package com.collection; import java.util.TreeSet; import java.util.Iterator; import java.util.Set; public class Collection { public static void main(String[] args) { //treeSet
底层存储数据的结构是一个哈希表 元素无序,不可重复 Set treeSet = new TreeSet(); treeSet.add("AAA"); treeSet.add("BBB"); treeSet.add("CCC"); treeSet.add("DDD"); treeSet.add("AAA"); treeSet.add("CCC"); //集合直接打印 System.out.print("----treeSet集合直接打印----" + "\n" + treeSet + "\t"); //Set没有带索引的方法,因此不能使用普通for循环 //迭代器打印 System.out.println("\n----treeSet集合迭代器打印----"); Iterator iterator = treeSet.iterator(); while (iterator.hasNext()) { System.out.print(iterator.next() + "\t"); } //for-each循环打印 System.out.println("\n----treeSet集合for-each循环打印----"); for (Object i : treeSet) { System.out.print(i + "\t"); } /* ----treeSet集合直接打印---- [AAA, BBB, CCC, DDD] ----treeSet集合迭代器打印---- AAA BBB CCC DDD ----treeSet集合for-each循环打印---- AAA BBB CCC DDD */ } } Tree Set详解(有参构造与无参构造)
//自定义人员类 package com.collection; public class Person implements Comparable
{ private String name; private int age; public Person() { } public Person(String name, int age) { 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; } //TreeSet接口是间接的实现了Set接口。TreeSet集合的元素使用Compilable接口进行自然排序。 //这个自然排序的类称之为自然排序类,该类的compileTo方法称为自然排序方法 //Compilable接口会对实现它的每一个类强加一个自然排序 //因此自定义的学生类要先实现Compilable接口,重写compareTo方法 @Override public int compareTo(Person p) { /* compareTo方法 返回0: 表示该元素与上一元素的对比结果为:两个元素相等 返回正数:表示该元素与上一元素的对比结果为:后者大于前者 返回负数:表示该元素与上一元素的对比结果为:前者大于后者 return 0; return 1; return -1; 如果compareTo方法返回结果写死是0,则视为TreeSet集合存储的元素全部相等。因此不会重复存储。这会导致输出TreeSet集合的值会只有第一个添加的元素 如果compareTo方法返回结果写死是正数,则视为TreeSet集合存储的元素后者始终大于前者,因此会按照添加的顺序存储。这导致会按照添加的顺序输出TreeSet集合的值 如果compareTo方法返回结果写死是负数,则视为TreeSet集合存储的元素后者始终小于前者,因此会按照添加的相反顺序存储。这导致会按照添加的相反顺序输出TreeSet集合的值 */ //需求:按照人员的年龄进行升序排序,年龄相等按照名字升序排序 int num = this.age - p.age; // this.age为后者,s.age为前者。如果num > 0则后者比前者年龄大 //compareTo方法本身就可以用于比较String类型字符串。例:a.compareTo(b):若a大于b,则返回正数,反之返回负数 //三元运算符:如果num=0,则返回this.name.compareTo(p.name) 的比较结果的值,如果num!=0,则返回num的值 return (num == 0 ? this.name.compareTo(p.name) : num); } } //TreeSet 测试类 package com.collection; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; public class TreeSetTest { public static void main(String[] args) { System.out.println("TreeSet集合使用无参构造定义"); TreeSetTestNoParameter(); System.out.println("TreeSet集合使用有参构造定义"); TreeSetTestHasParameter(); } public static void TreeSetTestNoParameter() { //TreeSet集合使用无参构造定义 Set treeSet = new TreeSet (); Person p1 = new Person("AAA", 20); Person p2 = new Person("BBB", 21); Person p3 = new Person("CCC", 22); Person p4 = new Person("DDD", 21); Person p5 = new Person("ABC", 22); Person p6 = new Person("ABC", 22); //添加赋值元素的值 treeSet.add(p1); treeSet.add(p2); treeSet.add(p3); treeSet.add(p4); treeSet.add(p5); treeSet.add(p6); for (Person i : treeSet) { System.out.println("Name:" + i.getName() + "\tAge:" + i.getName()); //只能通过getter()方法获取Perso类的私有成员变量 } /* TreeSet集合使用无参构造定义 Name:AAA Age:20 Name:BBB Age:21 Name:DDD Age:21 Name:ABC Age:22 Name:CCC Age:22 结论: 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对集合元素进行排序的 自然排序就是让元素所属的类(该例子为Person类)实现Compilable接口,重写CompileTo(T o)方法 重写方法时,可以根据排序要求的中药条件和次要条件来完成排序逻辑 */ } public static void TreeSetTestHasParameter() { //TreeSet集合使用有参构造定义 TreeSet treeSet = new TreeSet (new Comparator () { //匿名内部类 @Override public int compare(Person p1, Person p2) { //p1表示正在存入集合的元素,为后者。p2表示已存入集合的元素,为前者 int num = p1.getAge() - p2.getAge(); //判断量人员类元素的年龄是否相等 return (num == 0 ? p1.getName().compareTo(p2.getName()) : num); //三元运算符:判断如果年龄相等则比较名字,否则返回年龄比较的结果值 } }); Person p1 = new Person("AAA", 20); Person p2 = new Person("BBB", 21); Person p3 = new Person("CCC", 22); Person p4 = new Person("DDD", 21); Person p5 = new Person("ABC", 22); Person p6 = new Person("ABC", 22); //添加赋值元素的值 treeSet.add(p1); treeSet.add(p2); treeSet.add(p3); treeSet.add(p4); treeSet.add(p5); treeSet.add(p6); for (Person i : treeSet) { System.out.println("Name:" + i.getName() + "\tAge:" + i.getName()); } /* TreeSet集合使用有参构造定义 Name:AAA Age:20 Name:BBB Age:21 Name:DDD Age:21 Name:ABC Age:22 Name:CCC Age:22 结论: 用TreeSet集合存储自定义对象,有参构造方法使用的是比较器(Comparator)排序对集合元素进行排序的 比较器排序就是让集合构造方法接收Comparator的实现类对象,compare(T o1,T o2)方法 重写方法时,可以根据排序要求的主要条件和次要条件来完成排序逻辑 */ } }
Map接口
- Map接口存储一组键值对象,提供key(键)到value(值)的映射。Map
- 一张Map不能包含重复的键,每个键可以映射到至多一个值。
实现类
HashMap
- HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。
方法 描述 clear() 删除 hashMap 中的所有键/值对 clone() 复制一份 hashMap isEmpty() 判断 hashMap 是否为空 size() 计算 hashMap 中键/值对的数量 put() 将键/值对添加到 hashMap 中 putAll() 将所有键/值对添加到 hashMap 中 putIfAbsent() 如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。 remove() 删除 hashMap 中指定键 key 的映射关系 containsKey() 检查 hashMap 中是否存在指定的 key 对应的映射关系。 containsValue() 检查 hashMap 中是否存在指定的 value 对应的映射关系。 replace() 替换 hashMap 中是指定的 key 对应的 value。 replaceAll() 将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。 get() 获取指定 key 对应对 value getOrDefault() 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 forEach() 对 hashMap 中的每个映射执行指定的操作。 entrySet() 返回 hashMap 中所有映射项的集合集合视图。 keySet() 返回 hashMap 中所有 key 组成的集合视图。 values() 返回 hashMap 中存在的所有 value 值。 merge() 添加键值对到 hashMap 中 compute() 对 hashMap 中指定 key 的值进行重新计算 computeIfAbsent() 对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中 computeIfPresent() 对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。 代码
package com.collection; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; public class HashMap { public static void main(String[] args) { //HashMap 是无序的,即不会记录插入的顺序。 //存储的内容是键值对(key-value)映射 //一张Map不能包含重复的键,每个键可以映射到至多一个值。 java.util.HashMap
hashMap = new java.util.HashMap (); //添加元素值 hashMap.put(111, "AAA"); hashMap.put(222, "BBB"); hashMap.put(333, "CCC"); hashMap.put(444, "DDD"); System.out.println("--------直接打印hashMap的值------"); System.out.println(hashMap); /* --------直接打印hashMap的值------ {444=DDD, 333=CCC, 222=BBB, 111=AAA} */ //通过keySet()方法可以获取Map集合的key值集合视图,返回的是一个Set集合 System.out.println("--------打印hashMap的key集合视图------"); Set keySet = hashMap.keySet(); System.out.println(keySet); /* --------打印hashMap的key集合视图------ [444, 333, 222, 111] */ //通过values()方法可以获取Map集合的value值集合视图,返回的是一个Collection集合 System.out.println("--------打印hashMap的value集合视图------"); Collection values = hashMap.values(); for (Object v : values) { System.out.print(v + "\t"); } /* --------打印hashMap的value集合视图------ DDD CCC BBB AAA */ //通过entrySet()方法可以获取Map集合的key-value对集合视图,返回的是一个Set集合 System.out.println("\n--------打印hashMap的key-value对集合视图------"); Set > entrySet = hashMap.entrySet(); for (Map.Entry i : entrySet) { Integer num = i.getKey(); String str = i.getValue(); System.out.print(num + "=" + str + "\t"); } /* --------打印hashMap的key-value对集合视图------ 444=DDD 333=CCC 222=BBB 111=AAA */ System.out.println("\n--------打印hashMap的值(增强for遍历key集合视图)------"); //因为Map集合是通过键(key)来获取值(value),因此先通过 .keySet() 方法获取 hashMap 中所有 key 组成的集合视图。 //返回的集合试图没有索引,因此不能使用普通for循环遍历,只能使用增强for循环或者迭代器进行遍历 //遍历key集合视图,利用.get()方法以key作为参数获取hashMap集合的value值 for (Integer i : hashMap.keySet()) { System.out.print(i + "=" + hashMap.get(i) + "\t"); } /* --------打印hashMap的值(增强for遍历key集合视图)------ 444=DDD 333=CCC 222=BBB 111=AAA */ System.out.println("\n--------打印hashMap的值(迭代器遍历key集合视图)------"); Iterator iterator = keySet.iterator(); while (iterator.hasNext()) { Integer key = iterator.next(); System.out.print(key + "=" + hashMap.get(key) + "\t"); } /* --------打印hashMap的值(迭代器遍历key集合视图)------ 444=DDD 333=CCC 222=BBB 111=AAA */ System.out.println("\n--------再添加两个值--------"); hashMap.put(444, "EEEE"); hashMap.put(555, "CCCC"); System.out.println("--------添加值后直接打印hashMap的值------"); System.out.println(hashMap); /* --------再添加两个值-------- --------添加值后直接打印hashMap的值------ {555=CCCC, 444=EEEE, 333=CCC, 222=BBB, 111=AAA} */ //结论:因为Map集合会保证key值的唯一性,因此如果put入Map集合元素的key值与已有key值重复,则会将后者元素的value值覆盖掉相同key值元素映射的value值 //Map只是不允许元素key值重复,但是允许不同key值的元素对应的value值相等 } }