Java容器
容器
容器Collection, 底下有两个接口:Set 和 List
Set:无序不可重复
List:有序可重复
容器Map, 键值段, 一个key对应一个value
ArrayList, LinkedList, Vector
ArrayList底层实现是数组,所以查询较快,而插入较慢。(每插入一个值,之后的索引和值全部要依次延后)
LinkedList底层实现是链表,所以查询慢,而修改和删除块。链表结构是一个链着一个,查询的时候是挨个往后找,所以较慢。而插入新元素的时候只需要断开某个链,重连上新元素。所以修改插入快,而查询慢。
Vector,底层实现也是数组。线程安全,但效率低。ArrayList和LinkedList线程都不安全。
ArrayList
public class TestDemo { public static void main(String[] args) { //ArrayList容器 Employee e = new Employee(0001, "Jack", 5000, "Associate", "2017-12"); Employee e1 = new Employee(0002, "Rose", 2000, "Intern", "2020-12"); Employee e2 = new Employee(0003, "Tom", 10000, "Senior", "2012-12"); //泛型,这里的list就只能存储employee对象了 List<Employee> list = new ArrayList<Employee>(); list.add(e); list.add(e1); list.add(e2); } //这里的Employee类一般被称为Javabean,也称实体类(只有属性,构造器和相关getter, setter方法) }
HashMap, HashTable
HashMap线程不安全,效率高
HashTable线程安全,效率低
Map
public class TestDemo { public static void main(String[] args) { //一个map对象对应一行记录 Map map = new HashMap(); map.put("id", 0001); map.put("name", "Jack"); map.put("salary", 5000); map.put("title","Associate"); map.put("HireDate","2017-12"); Map map2 = new HashMap(); map.put("id", 0002); map.put("name", "Rose"); map.put("salary", 2000); map.put("title","Intern"); map.put("HireDate","2020-12"); Map map3 = new HashMap(); map.put("id", 0003); map.put("name", "Tom"); map.put("salary", 10000); map.put("title","Senior"); map.put("HireDate","2012-12"); } //不用专门构建类, 用类的属性做key
遍历List, 迭代器 Iterator
/** *遍历list和set */ public class TestDemo { public static void main(String[] args) { List list = new ArrayList(); list.add("aaaaa"); list.add("bbb"); list.add("ccccc"); //遍历一个list, 可以用for循环 for (int i=0; i<list.size();i++){ System.out.println(list.get(i)); } //如果是要遍历set, 因为set是无序的,所以for循环不好用了,用Iterator, 迭代器 Set set = new HashSet(); set.add("adsa"); set.add("adsadd"); set.add("adsdasdas"); Iterator iter = set.iterator(); while (iter.hasNext()){ String result = (String) iter.next(); System.out.println(result); } } }
遍历Map
/** * for each 和Interator遍历map */ public class Test { //for-each循环中遍历keys或values。 //如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。 Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //遍历map中的键 for (Integer key : map.keySet()) { System.out.println("Key = " + key);} //遍历map中的值 for (Integer value : map.values()) { System.out.println("Value = " + value); } //使用Iterator遍历 //1. 使用泛型: Map<Integer, Integer> map = new HashMap<Integer, Integer>(); Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<Integer, Integer> entry = entries.next(); System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); } //2. 不使用泛型: Map map = new HashMap(); Iterator entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Map.Entry) entries.next(); Integer key = (Integer)entry.getKey(); Integer value = (Integer)entry.getValue(); System.out.println("Key = " + key + ", Value = " + value); } }
泛型
泛型的作用:
1、安全:在编译的时候检查类型安全
2、方便:所有的强制类型转换都是自动的和隐式的,提高代码的重用率
/** 泛型类,在声明类时声明(加一个括号) T Type K V表示Key Value E 表示element ? 表示不确定类型 在使用时确定类型 注意: 1、泛型只能使用引用数据,不能是基本类型(只能是Integer, 不能是int) 2、泛型声明时字母不能使用静态属性,静态方法 **/ public class Student <T1, T2>{ private T1 numericScore; //数字分数 private T2 AlphabeticScore; //字母分数 public T1 getNumericScore() { return numericScore; } public void setNumericScore(T1 numericScore) { this.numericScore = numericScore; } public T2 getAlphabeticScore() { return AlphabeticScore; } public void setAlphabeticScore(T2 alphabeticScore) { AlphabeticScore = alphabeticScore; } public static void main(String[] args) { //使用时指定类型 Student<Double, String> stu = new Student<Double, String>(); //安全,类型检查 stu.setAlphabeticScore("A-"); stu.setNumericScore(92.33); //方便,自动类型转换 double it = stu.getNumericScore(); } } /** 泛型接口,字母只能使用在方法中,不能使用在全局变量 */ public interface Comparator<T>{ void compare(T t); }
HashMap经典储存,分拣,面向对象
/** *定义一个student类,属性为: name, classNumber, Score * 然后将若干student对象放入list中 * 统计每个班级的总分和平均分,分别打印出来 */ public class TestDemo { public static void main(String[] args) { List<Student> studentList = new ArrayList<Student>(); exam(studentList); Map<String, ClassRoom> rooms = new HashMap<String, ClassRoom>(); countScore(rooms,studentList); //打印总分,平均分 printScore(rooms); } /*** * 将student对象放入list */ public static void exam(List<Student> studentList){ studentList.add(new Student("a","001",80)); studentList.add(new Student("b","002",90)); studentList.add(new Student("c","002",70)); studentList.add(new Student("d","001",100)); studentList.add(new Student("e","003",78)); studentList.add(new Student("f","003",82)); studentList.add(new Student("g","003",96)); } /** *统计分数 */ public static void countScore(Map<String, ClassRoom> rooms, List<Student> studentList){ for(Student student: studentList){ String classNumber = student.getClassNumber(); double score = student.getScore(); //根据班级编号查看Map是否存在该班级,分拣思路 ClassRoom room = rooms.get(classNumber); if (null == room){ room = new ClassRoom(classNumber); rooms.put(classNumber, room); } //储存总分 room.setTotal(room.getTotal() + score); room.getStudentList().add(student);//加入学生 } } //打印总分与平均分 public static void printScore (Map<String, ClassRoom> rooms){ Set<Map.Entry<String, ClassRoom>> entrySet = rooms.entrySet(); Iterator<Map.Entry<String, ClassRoom>> iter = entrySet.iterator(); while (iter.hasNext()){ Map.Entry<String, ClassRoom> entry = iter.next(); ClassRoom room = entry.getValue(); double avg = room.getTotal() / room.getStudentList().size(); System.out.println("ClassNumber is " + room.getClassNumber() + " Total Score is " + room.getTotal() + " Average score is " + avg); } }
自定义类型排序
1、实体类: 实体类实现java.lang.Comparable接口 + 重写 compareTo方法
2、业务排序类:java.util.Comparator + compare
Comparable vs Comparator
Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。 Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。 用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。
推荐第二种,原因如下:
1、解耦:与实体类分离
2、方便应对多变的排序规则
第一种,Comparable
public class Person implements Comparable<Person> { String name; int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } @Override public int compareTo(Person p) { return this.age-p.getAge(); //重写compareTo方法,按年龄比较大小 } public static void main(String[] args) { Person[] people=new Person[]{new Person("xujian", 20),new Person("xiewei", 10)}; System.out.println("排序前"); for (Person person : people) { System.out.print(person.getName()+":"+person.getAge()); } Arrays.sort(people); System.out.println("\n排序后"); for (Person person : people) { System.out.print(person.getName()+":"+person.getAge()); } } }
第二种,comparator, 比较规则与实体类分离,解耦
/** * 自定义数据排序 */ public class GoodsApp { public static void main(String[] args) { List<Good> goods = new ArrayList<Good>(); goods.add(new Good("Car", 100000, 500)); goods.add(new Good("Plane", 1000000, 10)); goods.add(new Good("Bike", 100, 3248293)); System.out.println("排序前..." + "\n" + goods); System.out.println(); Collections.sort(goods, new GoodsPriceSort());//前面是需要排序的内容,后面是排序规则 System.out.println("排序后..." + "\n" + goods); } } //运行结果: 排序前... [GoodName: Car, AddToFavorite: 500, price: 100000.0 , GoodName: Plane, AddToFavorite: 10, price: 1000000.0 , GoodName: Bike, AddToFavorite: 3248293, price: 100.0 ] 排序后... [GoodName: Plane, AddToFavorite: 10, price: 1000000.0 , GoodName: Car, AddToFavorite: 500, price: 100000.0 , GoodName: Bike, AddToFavorite: 3248293, price: 100.0 ] /** * 一个简单的商品实体类 */ public class Good { private String name; private double price; private int addFavorite; public Good() { } public Good(String name, double price, int addFavorite) { this.name = name; this.price = price; this.addFavorite = addFavorite; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getAddFavorite() { return addFavorite; } public void setAddFavorite(int addFavorite) { this.addFavorite = addFavorite; } @Override public String toString() { return "GoodName: " + name +", AddToFavorite: " + this.addFavorite + ", price: " + this.price + "\n"; } } /** * 一个用来描述排序规则的类(按价格排序) */ public class GoodsPriceSort implements java.util.Comparator<Good>{ @Override public int compare(Good o1, Good o2) { //表示如果结果〉0,返回1,相等,返回0,否则返回1 //默认是升序,想要降序,所以前面加个负号 return -(o1.getPrice() - o2.getPrice()>0?1:o1.getPrice() == o2.getPrice()?0:-1); } }
TreeSet和TreeMap
TreeSet: 是Set接口的一个实现类,数据不可重复,数据元素可以排序。 对比HashSet: 元素必须重写hashcode和equals方法;
去重:元素比较为0则去重
排序的两种思路:
1、元素本身可以排序,使用Java.lang.Comparable + compareTo
2、排序业务类,用一个类定义排序规则,使用Java.util.Comparator + compare
TreeSet实际就是实现了Comparator接口的一个业务类
/** * 注意:TreeSet是在添加数据时排序,如果你更改数据,比如这里把路人甲的指数修改为101,并不改变原来的排序顺序 * 所以,在使用TreeSet时,不要修改数据,可以用Final严格修饰 */ public class TreeSetDemo { public static void main(String[] args) { Person p1 = new Person("You", 1); Person p2 = new Person("刘德华", 100); Person p3 = new Person("吴彦祖", 99); Person p4 = new Person("路人甲", 50); //依次放入TreeSet容器中,使用排序的业务类(匿名内部类) TreeSet<Person> persons = new TreeSet<Person>( new java.util.Comparator<Person>(){ public int compare(Person o1, Person o2){ return o1.getHandsomeIndex() - o2.getHandsomeIndex(); } } //匿名内部类 ); //TreeSet在添加数据时排序 persons.add(p1); persons.add(p2); persons.add(p3); persons.add(p4); System.out.println(persons); } } /** * 实体类实现Comparable接口,重写compareTo方法 */ public class Worker implements java.lang.Comparable<Worker> { private String position; private double salary; public Worker(String position, double salary) { this.position = position; this.salary = salary; } public Worker() { } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public int compareTo(Worker o) { return this.salary>o.salary?1:(this.salary==o.salary?0:-1); } } public class TreeSetDemo2 { public static void main(String[] args) { Worker w1 = new Worker("Clerk", 5000); Worker w2 = new Worker("Engineer", 10000); Worker w3 = new Worker("Manager", 30000); Worker w4 = new Worker("Head", 50000); TreeSet<Worker> workers = new TreeSet<Worker>(); workers.add(w1); workers.add(w2); workers.add(w3); workers.add(w4); System.out.println(workers); } }
相同点:
- TreeMap和TreeSet都是有序的集合,也就是说他们存储的值都是排好序的。
- TreeMap和TreeSet都是非同步集合,因此他们不能在多线程之间共享,不过可以使用方法Collections.synchroinzedMap()来实现同步
- 运行速度都要比Hash集合慢,他们内部对元素的操作时间复杂度为O(logN),而HashMap/HashSet则为O(1)。
不同点:
最主要的区别就是TreeSet和TreeMap分别实现Set和Map接口
- TreeSet只存储一个对象,而TreeMap存储两个对象Key和Value(仅仅key对象有序)
- TreeSet中不能有重复对象,而TreeMap中可以存在
- TreeMap的底层采用红黑树的实现,完成数据有序的插入,排序。
Collections
注意:Collection是一个上层接口,继承它的主要有Set和List
而Collections主要是一个工具类,提供了很多常见的,实用的方法:
Collections.reverse();
Collections.shuffle();
其他容器
队列:Queue单向队列,一端操作 / Deque双向队列,两端操作
/** * 使用单向队列Queue模拟银行排队存款 */ public class TreeSetDemo { public static void main(String[] args) { Queue<Request> que = new ArrayDeque<Request>(); //模拟排队情况 for(int i = 0; i<10; i++){ final int num = i; //因为匿名内部类只能访问final修饰的对象,所以这里需要这一步 //匿名内部类 que.offer(new Request() { //offer方法给和add方法都是添加一个元素,区别是当队列满了,add抛出异常,offer返回false @Override public void deposit() { System.out.println("No." + num + " is depositing..."); } }); } process(que); } interface Request{ void deposit(); } public static void process(Queue<Request> que){ Request request = null; while (null!=(request=que.poll())){ //poll方法给和remove方法都是删除第一个元素,区别是当队列为空,remove抛出异常,poll返回Null request.deposit(); } } } //运行结果 No.0 is depositing... No.1 is depositing... No.2 is depositing... No.3 is depositing... No.4 is depositing... No.5 is depositing... No.6 is depositing... No.7 is depositing... No.8 is depositing... No.9 is depositing...
Hashtable和其子类Properties
HashMap和Hashtable的区别:
1、* Hashtable线程安全,同步,效率低;HashMap线程不安全,非同步,效率高
2、两者父类不同,Hashtable - Dictionary; HashMap - AbstractMap
3、Hashtable键和值不能为null,HashMap键最多一个Null, 值可以多个Null
Properties:
1、经常用于读写配置文件
2、键和值只能为String
常用的方法:
setProperty(String key, String value)
getProperty(String key)
getProperty(String key, String DefaultValue)
存储
后缀:
.properties
- store(OutputStream out, String comments)
- store(Writer writer, String comments)
.xml
- storeToXML(OutputStream os, String comments) //默认使用UTF-8字符集
- storeToXMLOutputStream out, String comments, String encoding)
获取
loadFromXML(InputStream in)
public class TestDemo { public static void main(String[] args) throws FileNotFoundException, IOException { Properties pro = new Properties(); //存储 pro.setProperty("driver","oracle.jdbc.driver.OracleDriver"); pro.setProperty("url","jdbc:oracle:thin:@localhost:1521:orcl"); pro.setProperty("user","testUser"); pro.setProperty("pwd","123"); //存储到e:/others 绝对路径和盘符 //存储为properties文件,user = testUser, 直接对等 pro.store(new FileOutputStream(new File("e:/others/db.properties")),"db配置"); //存储为xml文件,xml格式 pro.storeToXML(new FileOutputStream(new File("e:/others/db.XMLproperties")),"dbXML配置"); //存储为相对路径,和项目摆在一起 pro.store(new FileOutputStream(new File("db.properties")),"db配置"); //不带盘符 pro.store(new FileOutputStream(new File("src/db.properties")),"db配置");//存到src folder底下 //读取 pro.load(new FileReader("e:/others/db.properties")); //绝对路径 pro.load(new FileReader("e:/src/db.properties")); //相对路径 //获取 String url = pro.getProperty("url","defaultUrl"); //根据类路径加载资源配置文件,工作中常用 pro.load(TestDemo.class.getResourceAsStream("/com/test/others/pro/db.properties")); // "/"表示bin目录 //当前线程的类加载器 pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/test/others/pro/db.properties") ); // ""表示bin目录 } }
容器的同步控制和只读设置
使容器可以线程安全,使用Collections.synchronizedList();
只读设置:
emptyXXX()
singletonXXX()
unmodifiableXXX()
Guava和Apache:对JDK容器的拓展,工作中常用的工具类
Guava
只读设置:ImmutableList.of
MultiSet.count() //可以计算每个元素出现的次数,非常方便。 //这里和HashSet不同,MultiSet也是无序,但是可重复
MultiMap //同样是键-值。但是键可以重复。//这样就使得我们可以把键和值对调,完成某些数据的统计
/** * Guava, Multimap */ public class TestDemo { public static void main(String[] args) { Map<String, String> courses = new HashMap<String, String>(); courses.put("Chinese", "Mr.Huang"); courses.put("English", "Mr.Huang"); courses.put("Math", "Mr.Wang"); courses.put("Physics","Mr.Wang"); courses.put("Chemistry", "Mr.Wang"); courses.put("Art", "Ms.Li"); //MultiMap Multimap<String, String> teachers = ArraryListMultimap.create(); //迭代器 Iterator<Map.Entry<String, String>> itr = courses.entrySet().iterator(); while (itr.hasNext()){ Map.Entry<String, String> entry = itr.next(); String key = entry.getKey(); String value = entry.getValue(); //转换键和值,存入MultiMap,因为MultiMap的键可以重复,所以不会报错 teachers.put(value, key); } //查看Multimap Set<String> keyset = teachers.KeySet(); for (String key: keyset){ Collection<String> collection = teachers.get(keys); System.out.println(key + "--->" + collection); } } } //输出结果 Mr.Huang ---> {Chinese, English} Mr.Wang ---> {Math, Physics, Chmistry} Ms.Li ---> {Art}
Bimap: 键和值都不能重复,Bimap.inverse() 非常实用,可以有效的使键值对调,从而简单的用值找键
浙公网安备 33010602011771号