Tinking in java 琐碎知识点之集合(容器)


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方法,以保证一致性)
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 琐碎知识点之集合(容器))