1、集合和数组都是对多个数据进行存储操作的结构,简称java容器
说明:此时的存储,主要是指内存层面的不涉及持久化的存储
2、数组特点:
一旦初始化后,长度确定了
一旦定义好,元素类型也确定了
缺点:
一旦初始化后,长度不可修改
数组中提供的方法有限,对于删除,插入数据等操作,非常不便,同时效率不高
获取数组中实际元素的个数无现成方法可用
数组存储数据的特点:有序、可重复,对于无序、不可重复的需求,无法满足
Collection接口:单列集合,用来存储一个一个的对象
List接口:存储有序的,可重复的数据
实现类:ArrayList、LinkedList、Vector
Set接口:存储无序的,不可重复的数据
实现类:HashSet、LinkedHashSet、TreeSet
Map接口:双列集合,用来存储一对一对(key - value)的数据
实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
Collection coll = new ArrayList(); coll.add("AA"); coll.add("BB"); coll.add(123); coll.add(new Date()); System.out.println(coll.size());//4 Collection coll1 = new ArrayList(); coll1.add(456); coll1.add("CC"); coll.addAll(coll1); //addAll(Collection c) System.out.println(coll.size());//6 System.out.println(coll.isEmpty());//false coll.clear(); System.out.println(coll.isEmpty());//true
Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); boolean contains = coll.contains(123); System.out.println(contains);//true //比较时会调用obj的equals方法 System.out.println(coll.contains(new String("Tom")));//true Collection coll1 = Arrays.asList(123,456); System.out.println(coll.containsAll(coll1));//true // boolean remove = coll.remove(123); // System.out.println(remove);//true // coll.removeAll(coll1); // //差集 // System.out.println(coll);//[Tom, false] //获取交集 Collection coll2 = Arrays.asList(123,456,789); // coll.retainAll(coll2); // System.out.println(coll);//[123, 456] //ArrayList有序 Collection coll3 = Arrays.asList(123,456,789); System.out.println(coll2.equals(coll3));//true coll3 = Arrays.asList(456,123,789); System.out.println(coll2.equals(coll3));//false System.out.println(coll.hashCode());//7639761 //集合->数组 Object[] objects = coll.toArray(); for (Object o: objects ) { System.out.println(o); } //数组 -> 集合 List<Object> objects1 = Arrays.asList(objects); Object[] objects2 = coll.toArray();
Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); Iterator iterator = coll.iterator(); // System.out.println(iterator.next());//123 //判断是否含有下一个元素 while (iterator.hasNext()){ //指针下移,并将元素返回 System.out.println(iterator.next()); }
ArrayList、LinkedList、Vector相同点:都实现了List接口,存储数据的特点相同,存储有序的、可重复的数据
ArrayList:作为List接口的主要实现类,线程不安全的,效率高,底层使用Object[] elementData存储
源码分析:
jdk7:
ArrayList list = new ArrayList();//底层创建了长度为10的Object[] elementData数组 list.add(123);//elementData[0] = new Integer(123); ... list.add(11);//如果此时的添加导致底层elementData数组容量不够,则扩容 //默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中 //结论:建议开发中使用带参的构造器,避免多次扩容 ArrayList list = new ArrayList(50);
jdk8中的变化:
ArrayList list = new ArrayList();//底层Object[] elementData数组初始化为{},并没有创建长度为10的数组 list.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将数据添加到elementData中 //扩容操作与jdk7.0一样 //延迟了数组的创建,节省了内存
Vector:作为List接口的古老实现类,线程安全的,效率低,底层使用Object[] elementData存储
初始化长度为10
LinkedList:对于频繁的插入和删除操作,效率比ArrayList高,底层使用双向链表存储
源码分析:
LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null list.add(123);//将123封装到Node中,创建了Node对象
List接口中常用方法
ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add(new String("Tom")); list.add(false); list.add(456); list.add(0,"BB"); System.out.println(list.toString());//[BB, 123, 456, Tom, false, 456] List list1 = Arrays.asList(1,2,3); list.addAll(list1); System.out.println(list.size());//9 int i = list.indexOf(456); System.out.println(i);//2 不存在返回-1 int i1 = list.lastIndexOf(456); System.out.println(i1);//5 Object remove = list.remove(0); System.out.println(remove);//BB System.out.println(list);//[123, 456, Tom, false, 456, 1, 2, 3] list.set(0,"AAA"); System.out.println(list);//[AAA, 456, Tom, false, 456, 1, 2, 3] List list2 = list.subList(0, 3); System.out.println(list2);//[AAA, 456, Tom] System.out.println(list);//[AAA, 456, Tom, false, 456, 1, 2, 3] System.out.println(list.get(1));//456
存储无序的、不可重复的数据
无序性:不等于随机性,以HashSet为例,存储的数据在底层数组中并非按照数组的索引的顺序添加,而是根据数据的哈希值
不可重复性:保证添加的元素按照equal方法判断时,不能返回true,即相同的元素只能添加一个
HashSet:作为Set接口的主要实现类,线程不安全的,可以存储null值,底层:数组+链表的结构
添加元素的过程:
我们向HashSet中添加元素a,首先调用元素a所在类的HashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出HashSet底层数组中的存放位置(即索引位置),判断数组此位置上是否已经有元素:
如果数组此位置上没有其它元素,则元素a添加成功。(1)
如果数组此位置上有其它元素b(或以链表形式存在的多个元素),则比较元素a和元素b的哈希值,如果哈希值不相同,则添加成功(2),如果相同,则需要调用元素a所在类的equals方法,若返回true,元素a添加失败,返回false,元素a添加成功。(3)
对于2和3添加成功的情况,元素a与已经存在指定索引位置上数据以链表的方式存储。
jdk7:元素a放在数组中,指向原来的元素
jdk8:原来的元素在数组中,指向元素a
(总结:七上八下)
1、Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
2、重写的hashCode()和equals()尽可能保持一致性,相等的对象必须具有相等的散列码
LinkedHashSet:HashSet的子类:遍历其内部,可以按照添加的顺序遍历,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet:使用红黑树存储,可以按照添加对象的指定属性进行排序
1、向TreeSet中添加的数据,要求是相同类的对象
2、两种排序方式:自然排序(实现Comparable接口)和定制排序
3、自然排序中,比较两个对象是否具有相同的标准为compareTo()返回0,不是equals()
4、定制排序中,比较两个对象是否具有相同的标准为compare()返回0,不是equals()
双列集合,用来存储一对一对(key - value)的数据
实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
HashMap:作为Map的主要实现类:线程不安全的,效率高,可以存储null的key和value
LinkedHashMap(1.4):HashMap的子类,保证遍历map元素时,可以按照添加的顺序实现遍历
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,执行效率高于HashMap
TreeMap:保证按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序和定制排序,底层使用红黑树
//向TreeMap中添加key-value,要求key必须是由同一个类创建的对象 //因为要按照key进行排序:自然排序、定制排序
Hashtable(1.0):作为古老的实现类:线程安全的,效率低,不能存储null的key和value
Properties: Hashtable的子类,常用来处理配置文件,key和value都是String类型
HashMap的底层:数组+链表(jdk7以前)
数组+链表+红黑树(jdk8)
Map结构:
Map中的key: 无序的,不可重复的,使用Set存储所有的key,key所在的类重写equals()和hashCode()
Map中的value: 无序的,可重复的,使用Collection存储所有的value,value所在的类重写equals()
一个键值对:key-value构成了一个entry对象
Map中的entry:无序的,不可重复的,使用Set存储所有的entry
HashMap的底层实现原理:
jdk7
HashMap map = new HashMap(); //实例化后,底层创建了长度为16的一维数组Entry[] table ... map.put(key1,value1); //首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。 //如果此位置上数据为空,此时的key1-value添加成功(1) //如果此位置上数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和意见存在的一个或多个数据的哈希值 //如果key1的哈希值和已经存在的数据的哈希值都不相同,此时的key1-value添加成功(2) //如果key1和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法 //如果equals()方法返回false,此时的key1-value添加成功(3) //如果equals()方法返回true,使用value1替换value2 //补充,对于2和3,此时的key1和value1和原来的数据以链表的方式存储 //扩容:当超出临界值(容量*加载因子)(且要存放的位置非空),默认扩容为原来容量的2倍,并将原有数据复制过来
jdk8不同点
HashMap map = new HashMap(); //1.底层没有创建一个长度为16的数组 //2.jdk 8底层的数组是Node[],而非Entry[] //3.首次调用put()方法时,底层创建长度为16的数组 //4.jdk底层结构只有数组+链表,jdk8中底层结构:数组+链表+红黑树,当数组的某一个索引位置上的元素以链表形式存在的数据个数>8,且当前数组的长度>64时,此时此索引位置上的所有数据改为红黑树存储,否则扩容(<64)。 //DEFAULT_INITIAL_CAPACITY:HashMap的默认容量:16 //DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75 //threshold:扩容的临界值 = 容量*加载因子:16*0.75 = 12 //TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8 //MIN_TREEIFY_CAPACITY:桶中的Node被树化是最小的hash表容量:64
LinkedHashMap
//可以按照加入的顺序遍历 //HashMap内部类:Node //LinkedHashMap内部类:Entry,继承了Node,且多了一堆引用,before ,after
Map接口常用方法:
Map map = new HashMap(); map.put("AA",123); map.put("BB",456); map.put("CC",123); map.put("DD",789); map.put("AA",333); System.out.println(map);//{AA=333, BB=456, CC=123, DD=789} Map map1 = new HashMap(); map1.put("EE",12); map1.put("FF",34); map.putAll(map1); System.out.println(map);//{AA=333, BB=456, CC=123, DD=789, EE=12, FF=34} Object cc = map.remove("CC"); System.out.println(cc);//123 System.out.println(map);//{AA=333, BB=456, DD=789, EE=12, FF=34} //map.clear(); //System.out.println(map.size());//0 System.out.println(map.get("BB"));//456 boolean isExist = map.containsKey("BB"); System.out.println(isExist);//true boolean b = map.containsValue(456); System.out.println(b);//true System.out.println(map.isEmpty());//false //遍历所有的key值 Set set = map.keySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } //遍历所有的value值 Collection values = map.values(); for (Object o : values){ System.out.println(o); } //遍历所有的entry Set set1 = map.entrySet(); Iterator iterator1 = set1.iterator(); while(iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry)obj; System.out.println(entry.getKey()+ "="+ entry.getValue()); }
//Collections:操作Collection和Map的工具类 List list = new ArrayList(); list.add(12); list.add(34); list.add(56); list.add(78); Collections.reverse(list); System.out.println(list);//[78, 56, 34, 12] Collections.shuffle(list); System.out.println(list);//[78, 56, 12, 34] Collections.sort(list); System.out.println(list);//[12, 34, 56, 78] Collections.swap(list,1,2); System.out.println(list);//[12, 56, 34, 78] System.out.println(Collections.max(list));//78 System.out.println(Collections.min(list));//12 System.out.println(Collections.frequency(list,12));//1 List dest = Arrays.asList(new Object[list.size()]); Collections.copy(dest,list); System.out.println(dest);//[12, 56, 34, 78] //转化为线程安全的list List list1 = Collections.synchronizedList(list);
//java.util.Arrays: 操作数组的工具类 int[] arr1 = new int[]{1,2,3,4}; int[] arr2 = new int[]{1,3,2,4}; System.out.println(Arrays.equals(arr1,arr2));//false System.out.println(Arrays.toString(arr1));//[1, 2, 3, 4] Arrays.fill(arr1,10); System.out.println(Arrays.toString(arr1));//[10, 10, 10, 10] Arrays.sort(arr2); System.out.println(arr2); int[] arr3 = {1,3,5,7,9}; System.out.println(Arrays.binarySearch(arr3,3));//1 System.out.println(Arrays.binarySearch(arr3,2));;//-2 未找到,负数 int[] ints = Arrays.copyOfRange(arr3, 0, 2); System.out.println(Arrays.toString(ints));//[1, 3] int[] ints1 = Arrays.copyOf(arr3, 2); System.out.println(Arrays.toString(ints1));//[1, 3]
原文:https://www.cnblogs.com/come-on-pxw/p/12790461.html