大数据开发成长之路——Java基础(四)

集合

  • Java中的集合是用来存放对象的容器
  • 集合可以存放不同类型,不限数量的数据
  • 分为两块:Collection和Map,也称之为集合接口
  • 集合类都存放在java.util包中

Collection

  • Collection分别被List和Set继承

List

  • List被AbstractList实现,然后分为3个子类,ArrayList,LinkedList和VectorList
  • List<E>是一种有序链表,本身是一个泛型接口,元素可以重复,可以是 null
  • 包含以下方法:
    在这里插入图片描述
  • 遍历List<E>
	// for循环
	List<String> list = ...;
	for (int i=0; i<list.size(); i++) {
		String s = list.get(i);	// 对linkList效率低
	}
	// 使用迭代器
	List<String> list = ...;
	for (Iterator<String> it = list.iterator(); it.hasNext();) {
		String s = list.next();	
	}
	// for...each
	// 除了数组,所有实现了Iterator接口的类都可以使用for...each
	List<String> list = ...;
	for (String s : list) {
		System.out.println(s);
	}
  • List和Array转换:
  1. toArray()方法
  2. Arrays.asList()方法
	// List转array
	List<Integer> list = new ArrayList<>();	// 实例化(不能new T)
	list.add(1);
	list.add(2);
	list.add(3);
	Integer[] arr = list.toArray(new Integer[list.size()]);	// 注意传入合适大小的数组
	// 集合只能存放对象。比如你存入一个int型数据放入集合中,其实它是自动转换成Integer类后存入的,Java中每一种基本数据类型都有对应的引用类型

	// array转List
	Integer arr = {1,2,3};
	List<Integer> arraylist = new ArrayList<>(Arrays.asList(arr));
	List<String> list = new ArrayList<>();
	list.add("A");list.add("b");list.add("c");
	list.contains("c");	// true		
	list.indexOf("c");	// 2	
	
	list.add(new String("C"));
	list.contains(new String("C"));	// true	
	// 上述结果是因为contains在判断时使用equals()方法,而不是“==”
	// 所以放入List中的类必须重写equals()方法(别用==),只要值相等就认为存在,即使是new的
	// Objects.equals()方法实现了对字符串的判断,包含了null的情况,可以借助
	// JDK提供的String、Integer类已经复写了equals方法,所以一般使用这些类型
  • ArrayList<E>
    • 动态添加元素的方式如下
      在这里插入图片描述
      在这里插入图片描述
  • LinkList<E>
    • 内部每个元素都有一个指针,指向下一个元素
      在这里插入图片描述
    • 添加元素时,只需新建结点,将末尾元素的指针指向即可
  • 通常情况下我们优先考虑使用ArrayList
    在这里插入图片描述
  • 队列和栈这一块都与LinkedList有关,就在这里介绍了:
  • Queue
    • Queue<E>实现一个先进先出的队列
    • LinkedList实现了Queue接口,所以可以将LinkedList作为Queue使用
      在这里插入图片描述
    • 为什么获取都有两种方法?
      在这里插入图片描述
    public static void main(String[] args) {
    	Queue<Person> queue = new LinkedList<>();	// 向上转型
    	queue.offer(new Person("Ming", 12));
    	queue.offer(new Person("Hong", 15));
    	queue.offer(new Person("Jun", 17));
    	System.out.println(queue.poll());
    	System.out.println(queue.poll());
    	System.out.println(queue.poll());
    	if(!isEmpty(queue)) {
    		System.out.println(queue.remove());
    	}	
    }
    
  • PriorityQueue
    public static void main(String[] args) {
    	// 使用优先级队列的类必须实现Comparable接口
    	// 也可以添加Comparator对象
    	Queue<Person> queue = new PriorityQueue<>();
    	queue.offer(new Person("Ming", 12));
    	queue.offer(new Person("Hong", 15));
    	queue.offer(new Person("Jun", 17));
    	System.out.println(queue.poll());	// 总是按优先级输出
    	System.out.println(queue.poll());
    	System.out.println(queue.poll());
    }
    
    public class Person implements Comparable<Person>{// 必须实现此接口
    	private final String name;
    	private final int age;
    
    	public Person(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    	public String getName() {
    		return name;
    	}
    	public int getAge() {
    		return age;
    	}
    	@Override
    	public String toString() {
    		return "(Person: " + name + ", " + age + ")";
    	}
    	public int compareTo(Person o) {
    		// 返回什么式子就按什么排序
    		return this.name.compareTo(o.name);	// 按name字段排序
    	}
    }
    
  • Deque
    • Deque实现一个双端队列(Double Ended Queue)
    • 既可以添加到队尾,也可以添加到队首;获取也是
      在这里插入图片描述
      在这里插入图片描述
    • 建议调用含Last/First的方法,方便区分
    	public static void main(String[] args) {
    		Deque<String> deque = new LinkedList<>();
    		deque.offerLast("end"); // "end"
    		deque.offerFirst("C"); // "C", "end"
    		deque.offerFirst("B"); // "B", "C", "end"
    		deque.offerFirst("A"); // "A", "B", "C", "end"
    		System.out.println(deque.pollLast());
    		System.out.println(deque.pollFirst());
    		System.out.println(deque.pollFirst());
    		System.out.println(deque.pollFirst());
    		System.out.println(deque.pollFirst());
    	}
    
  • Stack
    • 栈是一种后进先出的数据结构,计算机内存中普遍采用栈式结构
    • 用Deque可以实现栈的功能
    1. push():压栈
    2. pop():出栈
    3. peek():去栈顶元素但不出栈
    • 下面是中序表达式转后缀表达式的例子:
    // 后缀表达式是易于计算机运算的结构
    public static void main(String[] args) {
    	String s = "1 + 2 * (9 - 5)";
    	Calculator calc = new Calculator();
    	Object[] exp = calc.compile(s);
    	int result = calc.calculate(exp);
    	System.out.println("[calculate] " + s + " => " + expressionToString(exp) + " => " + result);
    }
    
    static String expressionToString(Object[] exp) {
    	List<String> list = new ArrayList<>(exp.length);
    	for (Object e : exp) {
    		list.add(e.toString());
    	}
    	return String.join(" ", list);
    }
    
    import java.util.ArrayList;
    import java.util.Deque;
    import java.util.LinkedList;
    import java.util.List;
    
    public class Calculator {
    	public Object[] compile(String s) {
    		Object[] parsed = parseAsExpression(s);
    		List<Object> output = new LinkedList<>();
    		Deque<Character> stack = new LinkedList<>();// 转型
    		for (Object e : parsed) {
    			if (e instanceof Integer) {
    				output.add(e);
    			} else {
    				char ch = (Character) e;
    				switch (ch) {
    				case ')':
    					// find '(' in stack:
    					for (;;) {
    						if (stack.isEmpty()) {
    							throw new IllegalStateException("Compile error: " + s);
    						}
    						char top = stack.pop();
    						if (top == '(') {
    							break;
    						} else {
    							output.add(top);
    						}
    					}
    					break;
    				case '(':
    					stack.push(ch);
    					break;
    				case '+':
    				case '-':
    				case '*':
    				case '/':
    					// find all operators that >= ch:
    					while (!stack.isEmpty()) {
    						char first = stack.peek();
    						if (priority(first) >= priority(ch)) {
    							stack.pop();
    							output.add(first);
    						} else {
    							break;
    						}
    					}
    					stack.push(ch);
    					break;
    				default:
    					throw new IllegalStateException("Compile error: " + s);
    				}
    			}
    		}
    		while (!stack.isEmpty()) {
    			output.add(stack.pop());
    		}
    		return output.toArray();
    	}
    	public int calculate(Object[] expression) {
    		Deque<Integer> stack = new LinkedList<>();
    		for (Object e : expression) {
    			if (e instanceof Integer) {
    				stack.push((Integer) e);
    			} else {
    				char op = (Character) e;
    				int n1 = stack.pop();
    				int n2 = stack.pop();
    				int r = operate(op, n2, n1);
    				stack.push(r);
    			}
    		}
    		return stack.pop();
    	}
    	Object[] parseAsExpression(String s) {
    		List<Object> list = new ArrayList<>();
    		for (char ch : s.toCharArray()) {
    			if (ch >= '0' && ch <= '9') {
    				int n = Integer.parseInt(String.valueOf(ch));
    				list.add(n);
    			} else if ("+-*/()".indexOf(ch) != (-1)) {
    				list.add(ch);
    			} else if (ch == ' ') {
    				// ignore white space
    			} else {
    				throw new IllegalArgumentException("Compile error: invalid char \'" + ch + "\'");
    			}
    		}
    		return list.toArray();
    	}
    	// priority from high to low: '*', '/' > '+', '-' > '('
    	int priority(char op) {
    		switch (op) {
    		case '*':
    		case '/':
    			return 2;
    		case '+':
    		case '-':
    			return 1;
    		case '(':
    			return 0;
    		default:
    			throw new IllegalArgumentException("bad operator: " + op);
    		}
    	}
    	int operate(char operator, int a, int b) {
    		switch (operator) {
    		case '+':
    			return a + b;
    		case '-':
    			return a - b;
    		case '*':
    			return a * b;
    		case '/':
    			return a / b;
    		default:
    			throw new UnsupportedOperationException();
    		}
    	}
    }
    

Set

  • Set<E>相当于不存储value的Map;用于去除重复元素
  • Set被AbstractSet实现,又分为2个子类,HashSet和TreeSet
  • 类似Map,TreeSet实现SortedSet,有序
  • 常用方法:
	import java.util.*;
	
	public static void main(String[] args) {
		List<String> list1 = Arrays.asList("pear", "apple", "banana", "orange", "apple", "banana");
		List<String> list2 = removeDuplicate(list1);
		System.out.println(list2);
	}
	
	static List<String> removeDuplicate(List<String> list) {
		Set<String> set = new HashSet<>(list);
		// Set<String> set = new TreeSet<>(list); 亦可以使用Comparator对象实现排序
		return new ArrayList<String>(set);
	}
  • 同样,放入Set的元素(类)要正确实现equals和hashCode方法
    图片引用自其他文章

Map

  • Map是一种键-值映射表
  • Map被AbstractMap实现,又分为2个子类,HashMap和TreeMap
  • HashMap不保证顺序
  • TreeMap实现的是SortedMap,所以可以保证按key有序
  • 常用方法:
	import java.util.*;
	
	List<Person> list = Arrays.asList(new Person("Ming", 12), new Person("Hong", 15), new Person("Jun", 18));
	Map<String, Person> map = new HashMap<>();	// 无序
	// Map<String, Person> map = new TreeMap<>();	// 有序
	//Map<String, Person> map = new TreeMap<>(new Comparator<String>() {// 指定顺序
    //        @Override
    //        public int compare(String o1, String o2) {
    //            return - o1.compareTo(o2);  // 倒序
    //        }
    //});
	for (Person p : list) {
		map.put(p.getName(), p);	// 存放键值对
	}
	System.out.println(map.get("Jun"));	// 通过k获取v
	System.out.println(map.get("Mark"));
	// key遍历	(for...each)
	for (String key : map.keySet()) {	// 生成键的set集合
       System.out.println(key+"-->"+map.get(key));
    }
    // entry遍历
    for (Map.Entry<String,Person> entry : map.entrySet()) {
        System.out.println(entry.getKey()+"-->"+entry.getValue());
    }
	public class Person {
		private final String name;
		private final int age;
	
		public Person(String name, int age) {
			this.name = name;
			this.age = age;
		}
		public String getName() {
			return name;
		}
		public int getAge() {
			return age;
		}
		@Override
		public String toString() {
			return "(Person: " + name + ", " + age + ")";
		}
	}
  • HashMap通过计算key的hashCode定位key的存储位置,继而得到value
  • 类似于List的contains()方法,凡是放入map的类必须重写equals()和hashCode()方法
  1. 只要参数相等即认为是相同对象,必须有相同的hashCode
  2. 只要参数不相等,则hashCode不等,避免冲突
public static void main(String[] args) {
	List<Person> list = Arrays.asList(new Person("Ming", 12), new Person("Hong", 15), new Person("Jun", 18));
	Map<Person, String> map = new HashMap<>();
	for (Person p : list) {
		map.put(p, p.getName());	// Person对象作为Key
	}
	// get方法判断的时候使用重写的equals(),然后得到相同的hashCode
	System.out.println(map.get(new Person("Jun", 18)));// true
}
  • 通过IDEA可以自动覆写方法
import java.util.Objects;

public class Person {

    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    @Override
    public boolean equals(Object o) {// 省略this
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;	// 强制转换
        return age == person.age &&
                Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
    	// 借助Object.hash实现
        return Objects.hash(name, age);// 保证了age和name相等时产生相同的hashCode
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString() {
        return "(Person: " + name + ", " + age + ")";
    }
}
// 总而言之:我们希望从成员值等即可判断相等,从而get到信息,这符合实际需求;与能否重复无关

Iterator迭代器

  • Iterator是一个用来遍历集合中元素的接口,主要方法:
  1. next():返回迭代器刚越过的元素的引用,返回值是Object,需要强制转换成自己需要的类型
  2. hasNext():判断容器内是否还有可供访问的元素
  3. remove():删除迭代器刚越过的元素
  • 它的子接口ListIterator在它的基础上又添加了三种方法,分别是add(),previous(),hasPrevious()
  • 我们可以通过实现Iterable接口,返回Iterator对象,让自己编写的类也能使用for…each循环
  • 参考如下代码:
	public static void main(String[] args) throws Exception {
		ReadOnlyList<String> list = new ReadOnlyList<>("apple", "pear", "orange");
		for (String s : list) {	// forEach
			System.out.println(s);
		}
	}
	import java.util.Iterator;

	public class ReadOnlyList<E> implements Iterable<E> {// 实现接口
		E[] array;
	
		@SafeVarargs
		public ReadOnlyList(E... array) {// 将外部数组存储在这
			this.array = array;
		}
	
		@Override
		public Iterator<E> iterator() {
			return new ReadOnlyIterator();// 返回Iterator对象
		}
		// 这里是重点:
		class ReadOnlyIterator implements Iterator<E> {// inner class,实现Iterator接口
			int index = 0;
			@Override
			public boolean hasNext() {
				// 可以获取外部类的属性:ReadOnlyList.this
				return index < ReadOnlyList.this.array.length;
			}
			@Override
			public E next() {
				E e = array[index];
				index++;
				return e;
			}
		}
	}
  • 调用者不需要知道集合内部结构,交给forEach即可以直接进行迭代!

Properties

  • Properties用于读写配置文件xxx.properties
  • .properties文件是key=value的形式,只能使用ASCII码
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
	public static void main(String[] args) throws Exception {
		Properties props = new Properties();
		props.load(Main.class.getResourceAsStream("/setting.properties"));
		String url = props.getProperty("url");
		String lang = props.getProperty("language");
		String title = props.getProperty("course.title");
		String description = props.getProperty("course.description");
		System.out.println(url);
		System.out.println(lang);
		System.out.println(title);
		System.out.println(description);
	}
// setting.properties
	# comment
	url=https://www.feiyangedu.com/
	language = Java
	course.title=Java\u96c6\u5408\u7c7b

Collections

  • Collections类提供了一组工具方便的使用集合类Collection
public class Practice {
       public static void main(String[] args){                             
		   List list = Arrays.asList("one two three four five six siven".split(""));//无空格         
		   System.out.println(list);
	       Collections.reverse(list);
	       System.out.println(list);
	       
		   List<String> list1 = new ArrayList<>(Arrays.asList("A", "B", "C"));
		   // 需要把上面的list1直接放在unmodifiableList中定义,才能保证只读性
	       List<String> readOnlyList = Collections.unmodifiableList(list1);
		   System.out.println(readOnlyList);
		   readOnlyList.add("X");
 	  }
}
  • 我们可以通过它创建、排序、shuffle集合等!

小结

  • 上面大致梳理了Java中集合的相关概念和用法,主要是List,Set,Map,以及相关的队列、栈、迭代器。后面介绍了两个实用的工具,配置读取类Properties和集合操作类Collections。集合是应用非常广泛的对象容器,需要多加体会!
  • 又是长篇大论,没办法,零基础小白只能点滴积累!
    看看宇宙,体会一下自己有多渺小
发布了9 篇原创文章 · 获赞 24 · 访问量 3730

猜你喜欢

转载自blog.csdn.net/weixin_39757637/article/details/105060796