集合操作(java.util*)
java.util.Collection
--------------->java.util.List
----------->java.util.ArrayList
------------>java.util.LinkedList
------------->java.util.Set
1.java.util.ArrayList
1.1 基本使用
底层操作就是一个数组(添加,移除,扩容)
基本使用 add ,size有效元素
添加元素
ArrayList list = new ArrayList();//不写泛型默认object 容纳任何对象。 Product p1 = new Product("001", "java課程1", 3000); list.add(p1);
查看有效元素的个数
System.out.println(list.size());
获取某个元素,根据数组下标获取
取第0个元素
System.out.println(list.get(0));
直接打印他的对象相当于打印他的ToString 直接遍历打印每个对象的tostring
System.out.println(list);
添加元素,放在第三个位置可以在但是下标不能大于数组的大小 ,不然就下标越界相当于在数组下标范围内,任意位置插入一个元素。
list.add(3,p1);
如果你是一个聚集,那么就会提供一个遍历和访问元素的方式。这个就和设计模式相关,称为迭代器模式。
删除第3个位置的元素
list.remove(3);
集合的遍历
//遍历方式1 for (int i = 0; i <list.size(); i++) { System.out.println(list.get(i)); }
方式二
/**遍历方式2迭代器遍历 *如果你是一个聚集,那么就会提供一个遍历和访问元素的方式。 * 并且不暴露集合的实现 * 这个就和设计模式相关,称为迭代器模式。 * ArrayList类已经提供了迭代器 * java.util.Iterator */ Iterator itor = list.iterator(); while (itor.hasNext()){ Product pro = (Product) itor.next(); System.out.println(pro); }
方式三
会把第一个给obj,再把第2个给obj,再把3个给obj一直到最后一个
// 遍历方式三 //foreach 遍历 for(Object obj :list){ System.out.println(obj); }
//判断某个元素是否包含在集合中用contains 方法
//判断某个元素是否包含在集合中 System.out.println(list.contains(p1));
contains 方法注意和实际场景的使用
可以进行重写hashCode 然后选择那些属性是相同的就是一个对象。
list集合中可以放重复的元素。
ArrayList 源代码
两个重要成员 Object[] EMPTY_ELEMENTDATA = {};
int size;
一个是空数组,一个是存数组长度。
其实数组的初始大小是10;
扩容,后移的操作。
移除元素的时候要做前移操作。
注意:ArrayList 这种有数组最为底层的操作,最好估算元素的多少来指定底层数组的大小。
ArrayList list = new ArrayList(给定大小);
避免数组扩容操作,因为扩容操作会产生垃圾,占用内存,性能较差。
1,2 java.util.Vector
操作都是类似的
Vector<String> vector = new Vector<>(); vector.add("hello"); vector.add("world"); System.out.println(vector.size()); System.out.println(vector);
默认大小也是10;
底层源代码 加了很多synchronized 关键字 ,区别就是一个线程安全一个线程不安全,
遍历方式都是一样,只不过是多了一种
for (int i = 0; i <vector.size() ; i++) { System.out.println(vector.get(i)); } for (String str:vector){ System.out.println(str); } Iterator<String> itor =vector.iterator(); while (itor.hasNext()){ String s = itor.next(); System.out.println(s); } //还有一种遍历方式 Enumeration <String> e=vector.elements(); while (e.hasMoreElements()){ System.out.println(e.nextElement()); }
底层也是数组的操作,大部分功能和ArrayList 类似。 有一种老的遍历方式 类似于 迭代器遍历。
ArrayList 线程不安全
Vector 线程安全
jdk7之前的版本扩容的大小稍有差异。
判断一个集合是否为空 isEmpty()
boolean b = vector.isEmpty();
2,LinkedList (链表结构)
2,1 基本使用和ArrayList 类似
LinkedList<String> list = new LinkedList<>(); list.add("mengzi1"); list.add("mengzi2"); list.add("mengzi3"); list.add("mengzi4"); // 有效元素的个数 System.out.println(list.size()); //移除一个元素 list.remove(2); //打印对象里的所有内容 System.out.println(list); //遍历 for (int i = 0; i <list.size() ; i++) { System.out.println(list.get(i)); } for (String str :list){ System.out.println(str); } Iterator<String> itor = list.iterator(); while (itor.hasNext()){ String next = itor.next(); System.out.println(next); }
LinkedList 是一个链表结构 提供了一个非常方便的 头尾操作。
不需要元素的移位。直接指向就可以了。
addFirst,addLast,getFirst,getLast
适合做一个 头尾操作比较频繁的事,比如 :贪吃蛇
3,java.util.HashSet 集合
set 集合不能放重复对象,是否重复由equals 方法决定
注意:容纳的对象必须根据自己的唯一标识来重写equals 和hashCode方法。
注意:对象一旦放入HashSet 容器中,对象的标识属性值不能再修改,否则倒置对象移除不了。
HashSet 存放元素的时候元素的位置是和对象hashCode算法得到相关的。
那么去查找元素时,也要根据这个算法的值,然后去找到相关的位置,移除元素,如果
没有元素存在该算法的属性值在存放容器后被修改了,意味着hashCode算法得到和之前的值不一样了,就删除不了,久而久之就会存在内存泄露。
3.2 HashSet 的数据结构
HashSet底层就是HashMap
基本使用添加,移除元素,遍历,和之前的一样。
4.java.util.TreeSet
注意:TreeSet中容纳的对象,必须能够排序(排序的方式自己定义)
java中有两种排序的比较器
4.1 java.lang.Comparable
构造函数如果无参数,那么TreeSet 使用无参的构造函数,那么容纳的对象必须实现 Comparable 接口
package corelesson3; import java.util.TreeSet; public class TreeSetDemo1 { public static void main(String[] args) { User user1= new User("menzi1","123123",18); User user2= new User("lisi","12",21); User user3= new User("lisi","12",20); TreeSet<User> ts = new TreeSet<User>(); ts.add(user1); ts.add(user2); ts.add(user3); System.out.println(ts); } }
package corelesson3; public class User implements Comparable<User> { private String name; private String pass; private int age; public User() { } public User(String name, String pass, int age) { this.name = name; this.pass = pass; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } @Override public int compareTo(User o) { if(!this.name.equals(o.name)) //根据年龄字母来排序 { return this.name.compareTo(o.name); }else //如果名字相同 根据年龄来排序 { return this.age-o.age; } //这里内部会自己比较,然后做交换,我们只需要定义谁比较谁就可以了 } }
4.2 java.util.Comparator
TreeSet 构造的时候使用Comparator 作为 构造函数的参数。
Comparator 是一个接口
package corelesson3; import java.util.Comparator; import java.util.TreeSet; public class TreeSetDemo2 { public static void main(String[] args) { User2 user1 = new User2("mengzi1","24234",23); User2 user2 = new User2("mengzi2","24234",23); User2 user3 = new User2("mengzi3","24234",23); TreeSet<User2> ts = new TreeSet<User2>(new MyComparator()); ts.add(user1); ts.add(user2); ts.add(user3); System.out.println(ts); // 使用匿名类的方式实现接口Comparator TreeSet<User2> ts1 = new TreeSet<User2>(new Comparator<User2>() { @Override public int compare(User2 o1, User2 o2) { return o2.getName().compareTo(o1.getName()); } }); System.out.println("===================="); ts1.add(user1); ts1.add(user2); ts1.add(user3); System.out.println(ts1); } //内部类方式实现接口Comparator private static class MyComparator implements Comparator<User2>{ @Override public int compare(User2 o1, User2 o2) { return o1.getName().compareTo(o2.getName()); } } }
两个对象 Comparator 比较 返回0 说明是同一个对象 是加不进容器里的
加入第一个,第二个是加不进去的。 添加,删除,遍历 ,和之前是一样的
4.3 数据接结构 底层是TreeMap
5,java.util.Map
-
java.util.HashMap(key/vaule)
5.1 基本操作
package corelesson3; import javax.activation.MailcapCommandMap; import java.net.ServerSocket; import java.util.HashMap; import java.util.Map; import java.util.Set; public class HashMapDemo1 { public static void main(String[] args) { User3 u1 = new User3("001", "mengzi1", 20, "123456"); User3 u2 = new User3("002", "mengzi2", 20, "123456"); User3 u3 = new User3("003", "mengzi3", 20, "123456"); User3 u4 = new User3("004", "mengzi4", 20, "123456"); HashMap<String ,User3> map = new HashMap<>(); map.put(u1.getId(),u1); map.put(u2.getId(),u2); map.put(u3.getId(),u3); map.put(u4.getId(),u4); //获取map 容器的大小 System.out.println(map.size()); //直接打印它的tostring ,打印全部内容 System.out.println(map); //获取某个元素 获得001 的信息 System.out.println(map.get("001")); System.out.println("=================================="); //判断某个key 是否存在 System.out.println(map.containsKey("002")); // 是否包含某个value System.out.println(map.containsValue(u2)); // 删除某个元素 User3 rm = map.remove("002"); /** * 遍历方式1: 把Map中所有的key 都放入set 集合中,然后遍历set 集合 * 然后遍历Set集合, 得到key,通过key 获取value */ System.out.println("=========================================="); Set<String> set =map.keySet(); for ( String key:set){ User3 u = map.get(key); System.out.println(key +"="+u); } System.out.println("============================="); /** * 遍历方式2 * 放入HashMap集合中的key,value 其实都会被包装成 * Map.Entry这个内部类的属性, * 有一个键值对就存在一个Map.Entry的实例对象 * 通过entrySet()方法就可以吧这些实例对象都放入Set集合 * 遍历Set 获取每一个对象。 *每一个类型都有getKey 和getValue */ Set<Map.Entry<String,User3>> set1=map.entrySet(); for(Map.Entry<String,User3> me:set1){ System.out.println(me.getKey()+"="+me.getValue()); } } }
注意: map.put(key,value) 如果key 相同的话 会覆盖前面的value值。
这个特点特别适合用来做计数,
map统计次数
package corelesson3; import java.util.HashMap; public class HashMapDemo3 { public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<>(); map.put("x",10); map.put("y",20); //后面x 的值 会覆盖前面的值 map.put("x",30); //这个特点适合用来做计数 System.out.println("===================================="); String s="asfdasdfsdyasdiobnvuyxzmsdfkshnmoivzvxbbnlajshkjtyierghacb"; /** * 计算每个字符出现的次数; * 思路:拿到每个字符,作为key 放入HashMap中, * 如果map 不中存在,那么put(key,1); * 如果已经存在 那么put(key,value+1); */ HashMap<String, Integer> map1 = new HashMap<>(); //1 遍历 for (int i = 0; i <s.length() ; i++) { String key=s.substring(i,i+1); if(!map1.containsKey(key)){ //不存在就放进去 map1.put(key,1); }else { //已经存在就加1 map1.put(key,map1.get(key)+1); } } System.out.println(map1); } }
package corelesson3; import java.util.HashMap; import java.util.Iterator; import java.util.TreeSet; public class HashMapDemo4 { public static void main(String[] args) { //要求计数,并且次数按照降序,如果次数相同 ,按照字母升序排序 /** * 思路: 通过HashMap 集合就可以实现计数操作 * 要排序 可以采用TreeSet 集合 * 并且是根据放入TreeSet 中的对象的属性进行排序的; * 抽取出一个类来完成这件事 */ String s="asfdasdfsdyasdiobnvuyxzmsdfkshnmoivzvxbbnlajshkjtyierghacb"; HashMap<String, Integer> map1 = new HashMap<>(); //1 遍历 for (int i = 0; i <s.length() ; i++) { String key=s.substring(i,i+1); if(!map1.containsKey(key)){ //不存在就放进去 map1.put(key,1); }else { //已经存在就加1 map1.put(key,map1.get(key)+1); } } //System.out.println(map1); /** * 遍历HashMap 集合,然后把每一个key/value 都包装成T对象的实例 * 放入TreeSet 集合,就完成了排序 */ TreeSet<T> ts = new TreeSet<>(); for(String key:map1.keySet()){ T t = new T(key,map1.get(key)); ts.add(t); System.out.println(); } //然后遍历这个集合,或者直接打印 System.out.println(ts); System.out.println("=================================="); for (T str:ts) { System.out.println(str); } System.out.println("========================"); //迭代器遍历 Iterator<T> itor = ts.iterator(); while (itor.hasNext()){ System.out.println(itor.next()); } } } class T implements Comparable<T>{ private String name; private int count; public T() { } public T(String name, int count) { this.name = name; this.count = count; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } @Override public String toString() { return name +": "+count +"次"; } //定义排序的规则 @Override public int compareTo(T o) { if(this.count!=o.count){ return o.count-this.count; }else { return this.name.compareTo(o.name); } } }
5.2 HashMap 数据结构
底层是一个数组加链表的结构,默认大小是16, 当hash值相同的时候就会放在链表里面,当链表程度大于8 的时候会转化为红黑树,当链表大小小于8 的时候,红黑树会转化为链表。
key/value 放入HashMap 的时候会被包装成Entry 的对象。
b,HashMap 的成员就有Entry的数组,该数组的大小默认是16,永远是2 的次方数
put(key,value) 时 其实就是转化成Entry对象并放入数组中。
c.put方法的实现
1,根据key 做一个hash 算法运算 得到一个hash值。
2,得到hash值去确定 数组的位置。 把hash,和数组长度 传过去。
做的事情就是 hash & (length-1)
等价于 hash % table.length length 是2 的次方数。
正好,获取到了数组的位置 (比如:3 %16 就拿到第三个数组的位置)
3 如果这个位置没有元素,直接包装Entry的实例并给数组赋值,如果计算出的位置已经有元素存在,就要判断是否相同,如果相同,则覆盖,并且要遍历整个链表,如果不覆盖,插入到链表头部。
如果计算出来的位置相同,这就是冲突率,我们要减少冲突率,因为一旦放入链表中,以后总是要遍历链表,效率差,要尽量把元素直接放入数组。而非链表。
所以要根据实际情况 重写hashCode和equals方法
注意2; 底层是数组,尽量减少扩容,所以HashMap 放入元素 也应该估算数组大小,避免扩容操作。
HashMap 中有加载因子 默认:0.75 扩到大2*length
5.3, get 方法实现
通过key 查找元素的算法和放入是一样的。
所以一打放入HashMap就不应该修改和hashCode和equals 方法生成相关属性的值。否则就会找不到。
6 , TreeMap (key/value)
key 必须要能够根据某种规则排序
通过两种比较器,TreeSe用底层就是TreeMap
添加 ,删除,遍历的方式
package corelesson3; import java.util.Comparator; import java.util.TreeMap; public class TreeMapDemo1 { public static void main(String[] args) { TreeMap<User, Integer> users = new TreeMap<>(); User user1= new User("menzi1","123123",18); User user2= new User("lisi","12",21); User user3= new User("lisi1","12",20); users.put(user1,1); users.put(user2,2); users.put(user3,3); // 遍历方式 两种 和hashmap 是一样的 for(User key:users.keySet()){ System.out.println(key+"="+users.get(key)); } System.out.println("============================="); //定义名字排序规则 TreeMap<User, Integer> users1= new TreeMap<>(new Comparator<User>() { @Override public int compare(User o1, User o2) { return o2.getName().compareTo(o1.getName()); } }); users1.put(user1,1); users1.put(user2,2); users1.put(user3,3); System.out.println(users1); } }
7.HashTable
和HashMap 相比,类的层次结构不一样。当然都实现了Map接口。基本操作都是类似。
package corelesson3; import java.util.HashMap; import java.util.Hashtable; public class HashTableDemo1 { public static void main(String[] args) { Hashtable<String, String> hs = new Hashtable<>(); // 添加元素 hs.put("aa","123"); hs.put("bb","456"); hs.put("cc","321"); hs.put("dd","876"); //移除元素 hs.remove("aa"); hs.remove("dd","876"); // 打印对象tostring System.out.println(hs); //遍历 for(String key:hs.keySet()){ System.out.println(key+"="+hs.get(key)); } HashMap<Object, Object> hm = new HashMap<>(); hm.put(null, null); System.out.println(hm); // hs.put(null,null); //会抛出 NullPointerException } }
7.2 hashMap 可以放 null的值
hashTable 不可以放 null 的值 会抛出NullPointerException 的异常
hashTable 底层源码有很多synchonized 的关键字。
7.3 Hashtable 是线程安全的
线程并发时效率低,但是能保证数据一致性
HashMap 是线程不安全的(线程并发时效率高,数据可能不一致,存在安全问题)
7.4 HashTable还有一个子类 Properties
java.util.Properties 类这个类在将来用的比较多,用来加载资源文件。
-
java.util.Collections 类
该类封装了一些对集合的操作都是静态方法。
面试有时会问和Collection类有什么关系,其实两个没有任何关系。
Collections 类封装了集合常用的静态方法
Collection 是集合类的上层接口,是 Set接口和List接口的父接口
package corelesson3; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class CollectionsDemo1 { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("aa"); list.add("bb"); list.add("ee"); list.add("cc"); list.add("dd"); //集合打印 System.out.println(list); //把里面的数组做倒置 Collections.reverse(list); System.out.println(list); System.out.println("============================="); // 给属数组做一个排序 Collections.sort(list); System.out.println(list); System.out.println("============================="); Collections.sort(list, new Comparator<String>() { @Override public int compare(String o1, String o2) { //做一个倒叙排序,改变比较的o1 和o2 位置即可 return o2.compareTo(o1); } }); System.out.println(list); ArrayList<Integer> list2 = new ArrayList<>(); list2.add(10); list2.add(20); list2.add(1000); list2.add(30); // 直接获取最大值 System.out.println(Collections.max(list2)); } }