Java集合像一种容器,可以动态的将每个对象的引用,放到容器中
Java集合可以分为两个体系:Collection、Map
Collection接口:单列数据,定义了一组对象的方法的集合 |-----List:元素有序、可重复 |-----ArrayList |-----LinkedList |-----Vector |-----Set:元素无序、不可重复 |-----HashSet |-----LinkedHashSet |-----TreeSet Map接口:双列数据,保存具有映射关系“key-----value对”的集合 |-----HashMap |-----LinkedHashMap |-----TreeMap |-----Hashtable |-----PropertiesCollection 接口:单列数据
Collection 的方法
//Collection 的方法 @test public void testArrayList(){ Collection coll = new ArrayList(); Collection coll1 = new ArrayList(); //add(object obj):将元素obj添加到coll中 coll.add("aa"); coll.add("bb"); coll1.add("aaa"); coll1.add("bbb"); //addAll(Collection coll1):将coll1集合中的元素添加进当前的集合中 coll.addAll(coll1); System.out.println(coll);//[aa, bb, aaa, bbb] //size():获取元素个数 System.out.println(coll.size());//2 //clear():清空集合元素 coll.clear(); //isEmpty():判断当前集合是否为空 System.out.println(coll.isEmpty());//false //contains(Object obj):集合中是否包含obj,返回布尔值,在判断时,会调用obj对象的equals方法 //所以要求obj对象的所在类需要重写equals方法 coll.add(123) System.out.println(coll.contains(123)); //containsAll(Collection coll):判断形参coll中的所有元素是否存在当前集合中 Collection coll2 = Arrays.asList(123,456); System.out.println(coll.containsAll(coll2));//false //remove(Object obj),移除成功返回true coll2.remove(123,456);//coll2 = [] //removeAll(Collection coll):差集:移除coll的全部元素 Collection coll3 = Arrays.asList(123); coll.removeAll(coll3);//coll = [] //retainAll(Collection coll4):与数组从coll4的交集 coll.add(123); coll.add(456); Collection coll4 = Arrays.asList(123,456); coll.retainAll(coll4);//所有的结果都是修改coll //equals(Object obj):当前集合和形参的元素都相同,返回true System.out.println(coll.equals(coll4)); //hasCode():返回当前方法的哈希值 System.out.println(coll.hashCode()); }数组和集合之间的相互转换:
@Test public void test(){ Collection coll = new ArrayList(); coll.add(123); coll.add("aa"); //集合------>数组 Object[] arr = coll.toArray(); for(int i = 0;i < arr.length;i++){ System.out.println(arr[i]); } //数组----->集合 List<String> list = Arrays.asList(new String[]{"AA","BB"}); System.out.println(list); //int这种基本数据类型只会作为一个元素,要使用int的包装类Integer }遍历: iterator():返回Iterator接口的实例,用于遍历集合元素 遍历集合元素的操作,使用迭代器Iterator接口:主要使用对象是Collection不包括Map
1.内部的方法: next() hasNext()2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。3.内部定义了remove(),可以在遍历得时候,删除集合中得元素,此方法不同于集合直接调用remove()方法 @Test public void testIterator(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add("AA"); //删除集合中AA这个数据 //如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。 Iterator iterator = coll.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); if("AA".equals(obj)){ iterator.remove(); } } while(iterator.hasNext()){ //next():作用一:指针下移,作用二:将下移以后集合位置上的元素返回 //hasNext():判断指针的下一个位置是否有元素,有元素返回True,默认指针在集合的第一个元素之前 System.out.println(iterator.next()); } }Java 5.0 提供了 foreach 循环迭代访问 Collection和数组
@Test public void testForeach(){ //集合的foreach Collection coll = new ArrayList(); coll.add(123); coll.add("AA"); //for(集合中元素的类型 局部变量 :集合对象),内部仍然调用了迭代器 for(Object obj : coll){ System.out.println(obj); } //数组的foreach int[] arr = new int[]{1,2,3,4,5,6,7}; //for(数组中元素的类型 局部变量 :数组对象) for(int i : arr){ System.out.println(i); } }List:元素有序、可重复的集合(类似于“动态数组”)
作为List接口的主要实现类,线程不安全,效率高,底层使用Object[] elemenData存储
底层源码分析: JDK7情况下:
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elemenData数据 list.add(123);//elementData[0] = new Integer(123); list.add(11);//如果此次添加导致底层的elementData 数组容量不够,扩容, 默认扩容为1.5倍,将原有数组中的数据复制到新的数组中 结论:建议开发中使用带参的构造器,ArrayList list = new ArrayList(int capacity)JDK8中:ArrayList的变化
ArrayList list = new ArrayList();//底层Object[]数组elemenData初始化为{},并没有创建长度为10的数组 list.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elemenData上 后续的添加和扩容操作与JDK7无异。小结 :
JDK7 ArrayList对象的创建相当于单例中的饿汉式,JDK8 ArrayList对象的创建相当于单例的懒汉式,延迟了数组的创建,节省内存。 /* 总结: 常用方法: 增:add(Object obj) 删:remove(int index)/remove(Object obj) 改:set(int index, Object ele) 查:get(int index) 插:add(int index, Object ele) 长度:.size() 遍历:1.Iterator迭代器方式 2.增强for循环 3.普通的循环(有索引) */ @test public void testArrayList(){ ArrayList list = new ArrayList(); list.add(123); list.add("AA"); //void add(int index, Object ele):在index位置插入ele元素 list.add(1,"BB");//list = [123,BB,AA] //boolean addAll(int index, Collection list1):从index位置开始将list1的所有元素添加进来 ArrayList list1 = Arrays.asList(1,2,3); list.add(list1);//[123,BB,AA,1,2,3] //Object get(int index):获取指定index位置的元素 System.out.println(list.get(0))//123 //int indexOf(Object obj):返回obj在集合中首次出现的位置 int i = list.indexOf(123); System.out.println(i);//0 //int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 int i1 = list.lastIndexof(AA); System.out.println(i1);//2 //Object remove(int index):移除指定index位置的元素,并返回此元素 Object re = list.remove(1);//BB //Object set(int index, Object ele):设置指定index位置的元素为ele list.set(1,"aa");//list = [123,aa,1,2,3] //List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合,左闭右开 System.out.println(list.subList(1, 3));//[aa,1] } @Test //对ArrayList的三种遍历方式: public void testIterator(){ ArrayList list = Arrays.asList(1,2,3,4,5,6,7); //method1: Iterator iterator = list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } //method2: for(Object obj : list){ System.out.println(obj); } //method3: for(int i = 0;i < list.size();i++){ System.out.println(list.get[i]); } }对于频繁插入、删除操作,使用此类效率比ArrayList效率高,底层使用双向链接存储。
LinkedList底层源码的分析
LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,默认值为null list.add(123);//将123封装到Node中,创建了Node对象。 其中Node定义为:体现了LinkedList的双向链表的说法。 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; } } /* LinkedList的新增方法: */ @Test public void testLinkedList(){ //创建集合LinkedList的对象linkedlist LinkedList<String> linkedlist = new LinkedList<String>(); // 添加元素 linkedList.add("hala") ; linkedList.add("madrid") ; linkedList.add("!!!") ; // 添加到第一个位置 linkedList.addFirst("CR7");//[CR7, hala, madrid] // 添加到最后一个位置 linkedList.addLast("KAKA");//[CR7, hala, madrid, KAKA] // 移除第一个元素:CR7 System.out.println(linkedList.removeFirst());//[hala, madrid, KAKA] // 移除最后一个元素:KAKA System.out.println(linkedList.removeLast());//[hala, madrid] // 获取第一个元素:hala System.out.println(linkedList.getFirst()); // 获取最后一个元素:madrid System.out.println(linkedList.getLast()); } }作为List接口的古老实现类,线程安全的,效率低,底层使用Object[] elemenData存储
Vector底层源码的分析:
JDK7和JDK8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组, 在扩容方面,默认扩容为原来数组长度的2倍。ArrayList、LinkedList、Vector的异同:
同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据不同:ArrayList、LinkedList、Vector中的底层源码分析。元素无序、不可重复的集合 -->高中讲的“集合”
Set接口中没有定义额外的方法,就是Collection里的方法
Set存储无序、不可重复的数据: ①无序:不等于随机型,存储的数据在底层数组中并非按照数组索引顺序添加,而是按照数据的哈希值确定的。 ②不可重复:保证添加元素的时候按照equals()方法判断,相同的元素只能添加一个Set中添加元素的过程,以HashSet为例说明:
初始底层数组长度16 1.向HashSet中添加元素a,首先调用元素a所在类的hascode()方法,计算元素a的哈希值, 此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置) 2.判断数组此位置上是否已经有元素,如果此位置没有元素,则元素a添加成功---情况1 3.如果此位置上有其他元素b(或者以链表形式存在的多个元素),则比较a与b的hash值: 如果hash值不同,则元素a添加成功,---情况2 如果hash值相同,进而需要调用元素a所在类的equals()方法: 返回true:元素a添加失败 返回false:元素a添加成功----情况3 对于添加成功的情况2和情况3,元素a与已经存在指定索引位置上数据以链表的方式存储。 总结:HashSet底层是数组加链表 JDK7中:a放在数组中,指向原来的元素 JDK7中:原来的元素放在数组中,指向a 要求:向set中添加数据时,对应的类一定要重写equals()和hashCode(Objectobj)方法, 重写的方法,尽可能保持一致性:相等的对象必须具有相同的散列码作为Set接口的主要实现类:不能保证元素的排列顺序,线程不安全,可以存储null值
HashSet 集合判断两个元素相等的标准: ①两个对象通过 hashCode() 方法比较相等 ②两个对象的 equals() 方法返回值也相等。
所以在添加数据时,如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
@Test public void testHashSet(){ Set set = new HashSet(); set.add(123); set.add("AA"); //注意FootballPlayer类要重写equals()和hashcode()方法 set.add(new FootballPlayer("卡卡","男","巴西")) Iterator iterator = Set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }LinkedHashSet作为我们HashSet的子类,再添加数据的同时,每个数据还维护了两个引用,记录此数据的前后的一个数据 优点:对于比较频繁的遍历操作,LinkedHashSet效率高于HashSet
@test public void testLinkedHashSet(){ Set set = new LinkedHashSet(); set.add(123); set.add("AA"); //注意FootballPlayer类要重写equals()和hashcode()方法 set.add(new FootballPlayer("卡卡","男","巴西")) for(Object obj : set){ System.out.println(obj); } }底层使用红黑树存储数据结构,向TreeSet中添加的数据要求必须是同类数据,不然没法比较
可以按照添加对象的指定属性进行排序
两种排序方式:①自然排序(默认情况下)②定制排序
自然排序:
TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系, 然后将集合元素按升序(默认情况)排列,所以该对象的实例必须实现Comparable接口 @Test public void testTreeSet(){ TreeSet set = new TreeSet(); set.add(200); set.add(-25); set.add(0); //添加失败:不能添加不同类的对象 //set.add("AA"); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }定制排序:
通过实现Comparator接口来实现,需要重写compare(T o1,T o2)方法 @test public void testComparator(){ Comparator con = new Comparator(){ @Override public int compare(Object o1,Object o2){ if(o1 instanceof Person && o2 instanceof Person){ Person p1 = (Person)o1; Person p2 = (Person)o2; return Integer.compare(p1.getAge(),P2.getAge()); }else{ throw new RuntimeException("输入的数据类型不一致"); } } }; TreeSet set = new TreeSet(); set.add(new Person("Jack",20)); set.add(new Person("Rose",18)); set.add(new Person("Jerry",4)); set.add(new Person("Tom",3)); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }Person类的定义:
public class Person { private String nama; private int age; public Person() { } public Person(String nama, int age) { this.nama = nama; this.age = age; } public String getNama() { return nama; } public void setNama(String nama) { this.nama = nama; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "nama='" + nama + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(nama, person.nama); } @Override public int hashCode() { return Objects.hash(nama, age); } }Comparable和Comparator异同:
共同点: 都可以进行排序,都可以自定义比较规则 两者都是返回一个描述对象之间关系的int 不同(面试题): Comparable 把自己和另一个对象进行比较 Comparator 比较两个不同的对象 Comparator与Comparable同时存在的情况下,比较器Comparator优先级高。 使用Comparable需要修改原先的实体类,是属于一种自然排序,而Comparator 是不用修改原先的类的 。Comparator实际应用广。双列数据,存储Key-Value对的数据。Key和Value可以是任何引用类型的数据
Key使用Set存放,不允许重复。要求所在类重写equals()、hashCode()方法 Value使用Collection存储所有的value。要求value所在的类要重写equals() Entry(键值对):key-value构成一个Entry对象,Map中的Entry;无序的、不可重复的,使用Set存储所有的Entry常用的实现类:HashMap、TreeMap、Hashtable、LinkedHashMap和Properties.
Map接口的常用方法:
@Test public void testMapMethod(){ Map map = new HashMap(); //Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中 map.put("AA",123); Map map1 = new HashMap(); map1.put("BB",456); map1.put("CC",789); //void putAll(Map m):将m中的所有key-value对存放到当前map中 map.putAll(map1); //Object remove(Object key):移除指定key的key-value对,并返回value Object value = map.remove("AA"); //Object get(Object key):获取指定key对应的value System.out.println(map.get("BB")); //boolean containsKey(Object key):是否包含指定的key System.out.println(map.containsKey("AA")); //boolean containsValue(Object value):是否包含指定的value System.out.println(map.containsValue(123)); //int size():返回map中key-value对的个数 System.out.println(map.size()); //void clear():清空当前map中的所有数据 //map.clear(); //boolean isEmpty():判断当前map是否为空 System.out.println(map.isEmpty()); //boolean equals(Object obj):判断当前map和参数对象obj是否相等 System.out.println(map.equals(map1)); //Set keySet():返回所有key构成的Set集合 //遍历所有的key集 Set set = map.keySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } //Collection values():返回所有value构成的Collection集合 //遍历所有的Value集 Collection coll = map.values(); for(Object obj : coll){ System.out.println(obj); } //Set entrySet():返回所有key-value对构成的Set集合 //遍历所有的Key-Value值对 //方式1: Set entrySet = map.entrySet(); Iterator iterator = entrySet.iterator(); while(iterator.hasNext()){ Object next = iterator1.next(); //entrySet集合中的元素都是entry Map.Entry entry = (Map.Entry) next; System.out.println(entry.getKey() + "-----" + entry.getValue()); } //方式2: Set keySet = map.keySet(); Iterator iterator keySet.iterator(); while(iterator.hasNext()){ Object key = iterator.next(); System.out.println(key + "-----" + map.get(key)); } }作为HashMap的子类,在HashMap的存储结构上使用一对双向链表来记录添加元素的顺序。
可以对Key-Value进行排序,可以保证所有的Key-Value对处于有序状态。 底层采用了红黑树结构存储数据。
排序和TreeSet一样,分为两类:自然排序和定制排序(按照Key来进行排序)
①自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口, 而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException ②定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对TreeMap 中的所有 key 进行排序。 此时不需要 Map 的 Key 实现Comparable 接口 @Test public void test1(){ TreeMap map = new TreeMap(); User u1 = new User("Tom",23); User u2 = new User("Jerry",32); User u3 = new User("Jack",20); User u4 = new User("Rose",18); map.put(u1,98); map.put(u2,89); map.put(u3,76); map.put(u4,100); Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while (iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); } } //定制排序 @Test public void test2(){ TreeMap map = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof User && o2 instanceof User){ User u1 = (User)o1; User u2 = (User)o2; return Integer.compare(u1.getAge(),u2.getAge()); }else{ throw new RuntimeException("输入的类型不匹配!"); } }); User u1 = new User("Tom",23); User u2 = new User("Jerry",32); User u3 = new User("Jack",20); User u4 = new User("Rose",18); map.put(u1,98); map.put(u2,89); map.put(u3,76); map.put(u4,100); Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while (iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); } }此时User类中需要实现Comparable接口
public class User implements Comparable{ private String name; private int age; public User() { } public User(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; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { System.out.println("User equals()...."); if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (age != user.age) return false; return name != null ? name.equals(user.name) : user.name == null; } @Override public int hashCode() { //return name.hashCode() + age; int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } //按照姓名从大到小排列,年龄从小到大排列 @Override public int compareTo(Object o) { if(o instanceof User){ User user = (User)o; // return -this.name.compareTo(user.name); int compare = -this.name.compareTo(user.name); if(compare != 0){ return compare; }else{ return Integer.compare(this.age,user.age); } }else{ throw new RuntimeException("输入的类型不匹配"); } } }Properties:常用来处理配置文件。key-value都是String类型
读取属性文件的流程 //创建一个properties的实例对象 Properties pros = new Properties(); //加载对应输入流流中的文件内容 pros.load(new FileInputStream("jdbc.properties")); //获取key值为user的value值 String user = pros.getProperty("user"); //输出显示 System.out.println(user);Collections:操作Collection、Map的工具类
@Test public void testList(){ List list = new ArrayList(); list.add(123); list.add(43); list.add(765); list.add(765); list.add(765); list.add(-97); list.add(0); //reverse(List):反转 List 中元素的顺序 Collections.reverse(list); //shuffle(List):对 List 集合元素进行随机排序 Collections.shuffle(list); //sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序 //sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 Collections.sort(list); //swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换 Collections.swap(list,1,2); //Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素 //Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素 Collections.max(list); //Object min(Collection) //Object min(Collection,Comparator) Collections.min(list); //int frequency(Collection,Object):返回指定集合中指定元素的出现次数 int frequency = Collections.frequency(list, 123); //void copy(List dest,List src):将src中的内容复制到dest中 List dest = Arrays.asList(new Object[List.size()]); Collections.copy(dest,list); //boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值 boolean ans = Collections.raplace(list,765,890);