Tinking in java 琐碎知识点之集合(容器)
2014-04-23 17:46
316 查看
1、Iterator必须依附于Collection接口的对象,Iterator本身不提供盛装对象的能力。Collectionname.iterator()方法获得Iterator接口实现类的对象
2、除了使用Iterator遍历Collection集合里的元素,也可以使用foreach循环(注意类型转换)
3、Set接口不允许包含重复元素(根据equals方法判断),而Collection没有此限制。Set接口的两个实现类HashSet是使用的哈希算法决定元素的存储位置,而treeSet使用的是红黑树算法。
4、Object类中定义了hashCode和equals方法,equals方法按照内存地址比较两个对象是否相等,hashCode方法返回值是根据内存地址计算出来的,所以同一个对象hashCode一样。HashSet是通过equas方法和hashCode方法判断两个元素是否相等的,将一个对象放入HashSet中时,HashSet会调用对象的的hashCode方法获得哈希码,再根据这个哈希码计算出对象在集合中存放的位置。因此如果重写了对应类的equals方法则也应该重写hashCode方法,保证如果2个对象通过equals比较相等那这两个对象的hashCode也应该一样,否则HashSet无法正常工作。
(即使两个对象equals方法返回true而hashCode不同也可以添加成功,使HashSet无法正常工作)
5、TreeSet集合是SortSet接口的唯一实现,TreeSet集合里的元素处于排序状态。试图把一个对象添加进TreeSet时,则该对象的类必须实现comparable接口,因为TreeSet会调用集合的元素的compareTo(Object obj)方法来比较元素之间的大小关系。
TreeSet自然排序是根据元素的大小以升序排序,也可以使用定制排序,即不用将类实现Comparable接口了,直接在创建TreeSet集合对象时提供一个Comparator对象作为TreeSet构造器参数,由该Comparator对象实现集合元素的排序逻辑。
6、compareTo(Object obj)方法返回一个整数值,如果obj1.compareTo(obj2)返回0,则表明这两个对象相等。返回一个正整数则表明obj1>obj2,如果返回一个负整数,则obj1<obj2
7、集合里的元素总是引用,但习惯上把被引用的对象称为集合元素
8、在实际应用中TreeSet集合里的对象属性被改变了不会重新排序,也就是说排序只是在元素加进集合时。所以最适合用treeSet排序的是不可变类。
9、为了保持Set规则的一致性,即Set集合中的元素总是不重复的,我们应该尽量保证两个对象通过equals方法和compareTo方法比较的结果一致(还要重写hashCode方法)。
10、关于Set的小结
HashSet性能比TreeSet好(特别是添加、查询操作),只有当要一个保持排序的Set时才应该使用TreeSet
EnumSet性能虽好,但是它只能保存同一个枚举类的枚举值作为集合参数
11、List接口
ArrayList和Vector都是List的实现类(Vector老了,虽然它是线程安全的都是也不推荐使用),ArrayList不是线程安全的,得手动控制,
Vector有个Stack子类,可以模拟栈这种数据结构。LinkedList也是一个List的实现类,它是基于链表实现的List
12、LinkedList和ArrayList、Vector的实现机制完全不同,ArrayList、Vector内部以数组的形式来保存集合中的元素,因此随机访问集合元素有较好的性能,而LinkedList内部以链表的形式来保存集合元素,随机访问性能差,但是插入删除元素时性能出色。
13、HashMap和Hashtable都是Map接口的实现类,他们的关系类似于ArrayList和Vector,Hashtable老了,用起来也不方便。另外Hashtable是线程安全的,HashMap是线程不安全的。(即使要使用线程安全的实现类也不必使用HashTable和Vector,有Collections工具)
14、为了在HashMap和Hashtable中存储、获取对象,用作key的对象必须实现hashCode和equals方法。HashMap和Hashtable判断两个key相等的标准是:两个key 通过equals方法返回true,通过hashCode方法返回值相等。而它们判断两个value是否相等则只要通过equals对象返回true即可。
(当重写了一个类的equals方法后,最好也重写一下hashCode方法,以保证一致性)
15、遍历Map中全部key-value对:调用Map对象的keySet方法返回全部key组成的Set对象,再通过遍历Set的元素(get方法获得key对应的 value)就可以遍历Map中所有的key-value对。
16、HashMap、Hashtable保存key的方式和HashSet保存集合元素的方式一至,所以HashMap、Hashtable对key的要求与HashSet对集合元素的要求相同。如果重写了该类的equals方法则应该也重写hashCode方法。
17、与HashSet类似的是,尽量不要使用可变对象作为HashMap、Hashtable的key,如果使用了可变对象,则尽量不要在程序中修改作为key的可变对象。
18、Hashtable有一个实用的子类Properties类,在处理属性文件时特别方便。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value写入属性文件,也可以把属性文件中的属性名、属性值加载到Map对象中。
例程:
19、正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类,Map接口有一个SortedMap子接口,SortedMap也有一个TreeMap实现类。与TreeSet类似,TreeMap也是基于红黑树对TreeMap中的所有key进行排序,从而保证TreeMap中所有key-value对处于有序状态。TreeMap也有自然排序(key所在的类实现Comparable接口、所有key应该是同一个类的对象)和客户化(创建TreeMap时传入一个Comparator对象,该对象负责实现排序逻辑)排序两种方式。
20、IdentityHashMap与HashMap基本相似,只是在处理两个key是否相等时不一样:只有当两个key严格相等(key1==key2)时才会认为两个key相等
21、操作集合工具类Collections提供了一些方法对集合元素进行排序、查询和修改操作,还提供了将集合对象设置成不可变、对集合对象实现同步控制等方法。
22、泛型:JDK1.5支持的泛型很大程度上是为了让集合能记住其元素的类型,这样会带来许多好处(Java泛型可以保证如果程序在编译的时候没有警告,运行时就不会产生ClassCastException异常,集合中默认存放的都是object对象,取出来还得强制转型,使用泛型就不要了)。
泛型数组(Java允许创建无上限的通配符泛型数组):
23、注意:在使用带了泛型声明的接口、父类之后,在使用这些接口或父类时不能再包含类型形参(方法中的形参只有当定义方法时才使用数据形参,当调用(使用)方法时必须传入实际的数据;与此类似的是:类、接口中的类型参数只有在定义类、接口时才可以使用类型参数,当使用类、接口时应该为类型参数传入实际的类型)。
如上例继承Apple类时可以有两种写法
public class A extends Apple<String>{}
或者直接不写<T>即:public class A extends Apple{}
数组和泛型有所不同,假设Son是Father的一个子类型(子类或者子接口),那么Son[]依然是Father[]的一个子类型,但是C<Son>并不是C<Father>的一个子类型。
24、使用类型通配符
(设定类型通配符的上限)比如List<? extends Shape>表示所有Shape泛型List的父类,List集合的元素要么是Shape类型的要么是Shape的子类型都行
(设定类型形参的上限)也可以在定义类时设定类型上限:public class Apple<T extends Number>。
也可以使用泛型方法:
修饰符 <T,S> 返回值类型 方法名(形参列表)
{
//方法体
}
2、除了使用Iterator遍历Collection集合里的元素,也可以使用foreach循环(注意类型转换)
3、Set接口不允许包含重复元素(根据equals方法判断),而Collection没有此限制。Set接口的两个实现类HashSet是使用的哈希算法决定元素的存储位置,而treeSet使用的是红黑树算法。
4、Object类中定义了hashCode和equals方法,equals方法按照内存地址比较两个对象是否相等,hashCode方法返回值是根据内存地址计算出来的,所以同一个对象hashCode一样。HashSet是通过equas方法和hashCode方法判断两个元素是否相等的,将一个对象放入HashSet中时,HashSet会调用对象的的hashCode方法获得哈希码,再根据这个哈希码计算出对象在集合中存放的位置。因此如果重写了对应类的equals方法则也应该重写hashCode方法,保证如果2个对象通过equals比较相等那这两个对象的hashCode也应该一样,否则HashSet无法正常工作。
(即使两个对象equals方法返回true而hashCode不同也可以添加成功,使HashSet无法正常工作)
5、TreeSet集合是SortSet接口的唯一实现,TreeSet集合里的元素处于排序状态。试图把一个对象添加进TreeSet时,则该对象的类必须实现comparable接口,因为TreeSet会调用集合的元素的compareTo(Object obj)方法来比较元素之间的大小关系。
TreeSet自然排序是根据元素的大小以升序排序,也可以使用定制排序,即不用将类实现Comparable接口了,直接在创建TreeSet集合对象时提供一个Comparator对象作为TreeSet构造器参数,由该Comparator对象实现集合元素的排序逻辑。
6、compareTo(Object obj)方法返回一个整数值,如果obj1.compareTo(obj2)返回0,则表明这两个对象相等。返回一个正整数则表明obj1>obj2,如果返回一个负整数,则obj1<obj2
7、集合里的元素总是引用,但习惯上把被引用的对象称为集合元素
8、在实际应用中TreeSet集合里的对象属性被改变了不会重新排序,也就是说排序只是在元素加进集合时。所以最适合用treeSet排序的是不可变类。
9、为了保持Set规则的一致性,即Set集合中的元素总是不重复的,我们应该尽量保证两个对象通过equals方法和compareTo方法比较的结果一致(还要重写hashCode方法)。
10、关于Set的小结
HashSet性能比TreeSet好(特别是添加、查询操作),只有当要一个保持排序的Set时才应该使用TreeSet
EnumSet性能虽好,但是它只能保存同一个枚举类的枚举值作为集合参数
11、List接口
ArrayList和Vector都是List的实现类(Vector老了,虽然它是线程安全的都是也不推荐使用),ArrayList不是线程安全的,得手动控制,
Vector有个Stack子类,可以模拟栈这种数据结构。LinkedList也是一个List的实现类,它是基于链表实现的List
12、LinkedList和ArrayList、Vector的实现机制完全不同,ArrayList、Vector内部以数组的形式来保存集合中的元素,因此随机访问集合元素有较好的性能,而LinkedList内部以链表的形式来保存集合元素,随机访问性能差,但是插入删除元素时性能出色。
13、HashMap和Hashtable都是Map接口的实现类,他们的关系类似于ArrayList和Vector,Hashtable老了,用起来也不方便。另外Hashtable是线程安全的,HashMap是线程不安全的。(即使要使用线程安全的实现类也不必使用HashTable和Vector,有Collections工具)
14、为了在HashMap和Hashtable中存储、获取对象,用作key的对象必须实现hashCode和equals方法。HashMap和Hashtable判断两个key相等的标准是:两个key 通过equals方法返回true,通过hashCode方法返回值相等。而它们判断两个value是否相等则只要通过equals对象返回true即可。
(当重写了一个类的equals方法后,最好也重写一下hashCode方法,以保证一致性)
public class MapTest { public static void main(String[] args) { HashMap<PerId,Person> hm=new HashMap<PerId,Person>(); hm.put(new PerId(0001), new Person("周鹏程1",25)); hm.put(new PerId(0002), new Person("周鹏程2",25)); hm.put(new PerId(0003), new Person("周鹏程3",26)); hm.put(new PerId(0004), new Person("周鹏程",27)); hm.put(new PerId(0005), new Person("周鹏程",27)); System.out.println(hm.containsKey(new PerId(0004))); System.out.println(hm.containsValue( new Person("周鹏程1",25))); System.out.println(hm.remove(new PerId(0001))); for(Object o:hm.keySet()){ System.out.println(hm.get(o).name); } } } class Person{ String name; int age; public Person(String name,int age){ this.name=name; this.age=age; } public boolean equals(Object obj){ if(obj==this){ return true; } if(obj!=null&&obj.getClass()==Person.class){ Person p=(Person)obj; if(p.age==this.age&&p.name==this.name){ return true; } } return false; } } class PerId{ int id; public PerId(int id){ this.id=id; } public boolean equals(Object obj){ if(obj==this){ return true; } if(obj!=null&&obj.getClass()==PerId.class){ PerId p=(PerId)obj; if(p.id==this.id){ return true; } } return false; } //假如没有重写hashCode方法将会出现问题 public int hashCode(){ return id; } }
15、遍历Map中全部key-value对:调用Map对象的keySet方法返回全部key组成的Set对象,再通过遍历Set的元素(get方法获得key对应的 value)就可以遍历Map中所有的key-value对。
16、HashMap、Hashtable保存key的方式和HashSet保存集合元素的方式一至,所以HashMap、Hashtable对key的要求与HashSet对集合元素的要求相同。如果重写了该类的equals方法则应该也重写hashCode方法。
17、与HashSet类似的是,尽量不要使用可变对象作为HashMap、Hashtable的key,如果使用了可变对象,则尽量不要在程序中修改作为key的可变对象。
18、Hashtable有一个实用的子类Properties类,在处理属性文件时特别方便。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value写入属性文件,也可以把属性文件中的属性名、属性值加载到Map对象中。
例程:
import java.io.*; import java.util.*; public class TestProperties { public static void main(String[] args)throws Exception { Properties props=new Properties(); props.setProperty("username", "zpc"); props.setProperty("password", "123456"); //将Properties中的属性保存到a.ini对象中 props.store(new FileOutputStream("a.ini"), "comment line(属性文件)"); Properties props2=new Properties(); props2.setProperty("gender", "male"); props2.load(new FileInputStream("a.ini")); System.out.println(props2); } } //输出:{password=123456, gender=male, username=zpc}
19、正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类,Map接口有一个SortedMap子接口,SortedMap也有一个TreeMap实现类。与TreeSet类似,TreeMap也是基于红黑树对TreeMap中的所有key进行排序,从而保证TreeMap中所有key-value对处于有序状态。TreeMap也有自然排序(key所在的类实现Comparable接口、所有key应该是同一个类的对象)和客户化(创建TreeMap时传入一个Comparator对象,该对象负责实现排序逻辑)排序两种方式。
20、IdentityHashMap与HashMap基本相似,只是在处理两个key是否相等时不一样:只有当两个key严格相等(key1==key2)时才会认为两个key相等
public static void main(String[] args) { IdentityHashMap ihm=new IdentityHashMap(); ihm.put(new String("语文"),89); ihm.put(new String("语文"),78); ihm.put("java",89); ihm.put("java",89); System.out.println(ihm); }//“java”字符串是字符串直接量,放在缓存,而new的两个对象用==比较不等
21、操作集合工具类Collections提供了一些方法对集合元素进行排序、查询和修改操作,还提供了将集合对象设置成不可变、对集合对象实现同步控制等方法。
//showHand游戏 import java.util.*; class ArrayUtils { /** * 定义一个工具方法,工具方法从字符串数组中找到对应的字符串元素的位置 * * @param array * 搜索的数组 * @param target * 搜索的字符串 * @return 目标字符串出现的位置,-1表明找不到 */ public static int search(String[] array, String target) { for (int i = 0; i < array.length; i++) { if (array[i] != null && array[i].equals(target)) { return i; } } return -1; } } public class ShowHand { private final int PLAY_NUM = 4;// 限定玩家数量 private String[] types = { "\4 ", "\5", "\3", "\6" };//特殊字符,会在控制台打印出方块、草花、红心和黑桃 private String[] values = { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" }; // cards存放牌 private List<String> cards = new LinkedList<String>(); // 用一个数组存放玩家 private String[] players = new String[PLAY_NUM]; // 每一个玩家都用一个List存放其获得的牌 private List<String>[] playersCards = new List[PLAY_NUM]; // 初始化,放入扑克牌 public void initCards() { for (int i = 0; i < types.length; i++) { for (int j = 0; j < values.length; j++) { cards.add(types[i] + values[j]); } } // 调用集合工具类的方法随机排列 Collections.shuffle(cards); } // 初始化玩家,为每个玩家分配用户名 public void initPlayer(String... names) { if (names.length < 2 || names.length > PLAY_NUM) { System.out.println("输入玩家数量不对!"); return; } else { // 初始化用户 for (int i = 0; i < names.length; i++) { players[i] = names[i]; } } } // 初始化保存玩家牌的List public void initPlayerCards() { for (int i = 0; i < players.length; i++) { if (players[i] != null && !players.equals("")) { playersCards[i] = new LinkedList<String>(); } } } // 写一个输出全部扑克牌的方法 public void showAllCards() { for (String card : cards) { System.out.println(card); } } // 给玩家派牌的方法(指定一个最先派牌的玩家) public void deliverCard(String first) { int firstPos = ArrayUtils.search(players, first); //依次给位于指定玩家之后、之前的每个玩家派牌 for(int i=firstPos;i<players.length;i++){ if(players[i]!=null){ playersCards[i].add(cards.get(0)); cards.remove(0); } } //为之前的每个玩家派牌 for(int i=0;i<firstPos;i++){ if(players[i]!=null){ playersCards[i].add(cards.get(0)); cards.remove(0); } } } //输出玩家手中的牌 public void showPlayerCards(){ System.out.print("当前牌况:"); for(int i=0;i<players.length;i++){ if(players[i]!=null){ System.out.println("\n"+players[i]+":"); for(String card:playersCards[i]){ System.out.print (card+"\t"); } } } System.out.print("\n"); } public static void main(String[] args) { ShowHand sh = new ShowHand(); sh.initCards(); sh.initPlayer("鸟鹏", "鸟朱"); sh.initPlayerCards(); sh.showAllCards(); System.out.println("*****************"); //第一次从玩家鸟鹏开始派牌 sh.deliverCard("鸟鹏"); sh.showPlayerCards(); //第二次从玩家鸟朱开始派牌 sh.deliverCard("鸟朱"); sh.showPlayerCards(); } }
22、泛型:JDK1.5支持的泛型很大程度上是为了让集合能记住其元素的类型,这样会带来许多好处(Java泛型可以保证如果程序在编译的时候没有警告,运行时就不会产生ClassCastException异常,集合中默认存放的都是object对象,取出来还得强制转型,使用泛型就不要了)。
//一个泛型的案例 public class Apple<T> { private T info; public Apple() { } public Apple(T info){ this.info=info; } public T getInfo(){ return info; } public static void main(String[] args){ //因为传给T形参的是String实际类型,所以构造器的参数只能是String Apple<String> apple1=new Apple<String>("苹果"); System.out.println(apple1.getInfo()); System.out.println(apple1.getClass()); Apple<Double> apple2=new Apple<Double>(12.9); System.out.println(apple2.getInfo()); System.out.println(apple2.getClass()); } } //输出 //苹果 //12.9
泛型数组(Java允许创建无上限的通配符泛型数组):
List<?>[] lsa= new ArrayList<?>[10]; Object[] oa=(Object[])lsa; List<Integer> li=new ArrayList<Integer>(); li.add(new Integer(7)); oa[1]=li; System.out.println("lsa.getClass():"+lsa.getClass()); System.out.println("lsa[1].get(0).getClass():"+lsa[1].get(0).getClass()); //String s=(String)lsa[1].get(0);//List的get方法 Object target=lsa[1].get(0); if(target instanceof String){ String s=(String)target; }
23、注意:在使用带了泛型声明的接口、父类之后,在使用这些接口或父类时不能再包含类型形参(方法中的形参只有当定义方法时才使用数据形参,当调用(使用)方法时必须传入实际的数据;与此类似的是:类、接口中的类型参数只有在定义类、接口时才可以使用类型参数,当使用类、接口时应该为类型参数传入实际的类型)。
如上例继承Apple类时可以有两种写法
public class A extends Apple<String>{}
或者直接不写<T>即:public class A extends Apple{}
数组和泛型有所不同,假设Son是Father的一个子类型(子类或者子接口),那么Son[]依然是Father[]的一个子类型,但是C<Son>并不是C<Father>的一个子类型。
24、使用类型通配符
(设定类型通配符的上限)比如List<? extends Shape>表示所有Shape泛型List的父类,List集合的元素要么是Shape类型的要么是Shape的子类型都行
(设定类型形参的上限)也可以在定义类时设定类型上限:public class Apple<T extends Number>。
也可以使用泛型方法:
修饰符 <T,S> 返回值类型 方法名(形参列表)
{
//方法体
}
例程: abstract class Shape{ public abstract void draw(Canvas c); } class Circle extends Shape{ @Override public void draw(Canvas c) { System.out.println("在画布"+c+"画一个圆"); } } class Rectangule extends Shape{ @Override public void draw(Canvas c) { System.out.println("在画布"+c+"画一个矩形"); } } public class Canvas { //同时在画布上绘制多个形状 //使用被限制的泛型通配符 public void drawAll(List<? extends Shape> shapes){ for(Shape s:shapes){ s.draw(this); } } public static void main(String[] args) { List<Circle> circleList=new ArrayList<Circle>(); List<Rectangule> rectangule=new ArrayList<Rectangule>(); circleList.add(new Circle()); rectangule.add(new Rectangule()); Canvas c=new Canvas(); c.drawAll(circleList); c.drawAll(rectangule); } }
相关文章推荐
- Tinking in java 琐碎知识点之反射
- JAVA集合(容器)类知识点汇总
- thingking in java 读书笔记---对象集合和容器
- Java基础 - 容器和集合
- 再读thinking in java -- 第十七章 容器深入研究(一)
- 牛客网刷题知识点总结(五)java容器
- Java 容器集合框架概览
- Java集合容器简介
- Java容器集合学习心得
- [Java] 容器-01 实现 Comparable 接口 / 重写 equals 与 hashCode (1个图 1个类 3个知识点 6个接口)
- Java进阶知识点6:并发容器背后的设计理念 - 锁分段
- java容器/集合框架
- 黑马程序员——Java基础---集合知识点笔记
- Java容器集合类的区别用法
- [think in java]知识点学习
- java 容器 集合 用法
- Java简单容器(集合)分类
- java 集合 容器 List Set Map区别
- JAVA容器集合简介1
- 【38】java的集合框架(容器框架)