Java解惑

1.遞歸中的坑

之前在leetCode上刷題,問題傳送門:Binary Tree Paths,按理說一個很簡單的遞歸方法就能解決啊,爲什麼會出現問題呢?先po代碼:

public class Solution {
public List<String> binaryTreePaths(TreeNode root) {
    List<String> answer = new ArrayList<String>();
    if (root != null) searchBT(root, "", answer);
    return answer;
}
private void searchBT(TreeNode root, String path, List<String> answer) {
     if (root.left == null && root.right == null) answer.add(path + root.val);
     if (root.left != null){
         path = path + root.val + "->";
         searchBT(root.left, path, answer);
     } 
     if (root.right != null){
         path = path + root.val + "->";
         searchBT(root.right, path, answer);
     } 
 }
}

上面的代碼就是會出錯,通過打log,發現path+="someString",雖然結果傳遞給searchBT方法沒有問題,但是卻產生了副作用:path的值也跟着改變了。這種錯誤爲什麼在平時的代碼中沒有遇到呢?我分析是因爲,平常程序很少有遞歸調用,從而忽略了這個錯誤。

關於java的傳值方式,我一直都沒能搞清楚,請看第6部分內容!

改成這樣就是想要的結果:

public class Solution {
 public List<String> binaryTreePaths(TreeNode root) {
    List<String> answer = new ArrayList<String>();
    if (root != null) searchBT(root, "", answer);
    return answer;
}
private void searchBT(TreeNode root, String path, List<String> answer) {
    if (root.left == null && root.right == null) answer.add(path + root.val);
    if (root.left != null) searchBT(root.left, path + root.val + "->", answer);
    if (root.right != null) searchBT(root.right, path + root.val + "->", answer);
}
}

問題解決!

2.Anonymous Classes

Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.

Anonymous Classes的語法:an anonymous class is an expression.The syntax of an anonymous class expression is like the invocation(調用) of a constructor, except that there is a class definition contained in a block of code.

示例代碼:

//其中HelloWorld是一個interface
HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

關於內部類和內部匿名類如何捕獲變量,引用外部類的變量,一直以來感覺都不太好理解,一些注意和爲什麼請看權威R大的答案

3.Lambda Expressions

有了匿名內部類的概念,如果我們要實現的匿名類很簡單,比如實現的接口只有一個方法,那麼專門寫一個匿名類就顯得很笨重啊,這時候Lambda Expressions就閃亮登場了!

Lambda expressions let you express instances of single-method classes more compactly.匿名類一定程度簡化了內部類,Lambda expressions一定程度上簡化了匿名類。

一個例子:

//Use Aggregate Operations That Accept 
//Lambda Expressions as Parameters
roster
    .stream()
    .filter(
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25)
    .map(p -> p.getEmailAddress())
    .forEach(email -> System.out.println(email));

Aggregate operations process elements from a stream, not directly from a collection (which is the reason why the first method invoked in this example is stream).

Syntax of Lambda Expressions

組成部分:
1.A comma-separated list of formal parameters enclosed in parentheses.
2.The arrow token, ->
3.A body, which consists of a single expression or a statement block.

Note that a lambda expression looks a lot like a method declaration; you can consider lambda expressions as anonymous methods—methods without a name.

一個示例:

public class Calculator {

    interface IntegerMath {
        int operation(int a, int b);
    }

    public int operateBinary(int a, int b, IntegerMath op) {
        return op.operation(a, b);
    }

    public static void main(String... args) {

        Calculator myApp = new Calculator();
        IntegerMath addition = (a, b) -> a + b;
        IntegerMath subtraction = (a, b) -> a - b;
        System.out.println("40 + 2 = " +
                myApp.operateBinary(40, 2, addition));
        System.out.println("20 - 10 = " +
                myApp.operateBinary(20, 10, subtraction));
    }
}

The method operateBinary performs a mathematical operation on two integer operands. The operation itself is specified by an instance of IntegerMath. The example defines two operations with lambda expressions, addition and subtraction. The example prints the following:

40 + 2 = 42
20 - 10 = 10

4.Method References

Suppose that the members of your social networking application are contained in an array, and you want to sort the array by age. You could use the following code:

List<Person> roster=new ArrayList<>();
        Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);

        class PersonAgeComparator implements Comparator<Person> {
            public int compare(Person a, Person b) {
                return a.getBirthday().compareTo(b.getBirthday());
            }
        }

        Arrays.sort(rosterAsArray, new PersonAgeComparator());

如何用Comparator來sort!

注意到sort方法:

static <T> void sort(T[] a, Comparator<? super T> c)

Notice that the interface Comparator is a functional interface. Therefore, you could use a lambda expression instead of defining and then creating a new instance of a class that implements Comparator:(對於該類interface可以簡化之)

Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);

However, this method to compare the birth dates of two Person instances already exists as Person.compareByAge. You can invoke this method instead in the body of the lambda expression:

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

Because this lambda expression invokes an existing method, you can use a method reference instead of a lambda expression:

Arrays.sort(rosterAsArray, Person::compareByAge);

這種寫法跟上面代碼完全等價。

Kinds of Method References

一共有四種:

1.Reference to a Static Method:
上面例子Person::compareByAge就是。
2.Reference to an Instance Method of a Particular Object:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }

    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

3.Reference to an Instance Method of an Arbitrary Object of a Particular Type:

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

4.Reference to a Constructor:

5.Collections類知識彙總

官方資料相關:
The Collections Framework

1.Overview

2.Outline of the Collections Framework(API reference)

3.Tutorial

4.Design FAQ

一個一個來看:

1-overview

爲什麼要設計Collections framework呢?它主要有以下幾種優點:

1.Reduces programming effort by providing data structures and algorithms so you don’t have to write them yourself.

2.Increases performance by providing high-performance implementations of data structures and algorithms. Because the various implementations of each interface are interchangeable(可相互交換的), programs can be tuned by switching implementations.

3.Provides interoperability(互用性) between unrelated APIs by establishing a common language to pass collections back and forth.

4.Reduces the effort required to learn APIs by requiring you to learn multiple ad hoc collection APIs.

5.Reduces the effort required to design and implement APIs by not requiring you to produce ad hoc collections APIs.

6.Fosters(培養) software reuse by providing a standard interface for collections and algorithms with which to manipulate them.

那麼這個framework主要由哪些部件組成呢?組成部分:

1.Collection interfaces. Represent different types of collections, such as sets, lists, and maps. These interfaces form the basis of the framework.

2.General-purpose implementations. Primary implementations of the collection interfaces.通用

3.Legacy implementations. The collection classes from earlier releases, Vector and Hashtable, were retrofitted(翻新) to implement the collection interfaces.歷史遺留實現,拿Hashtable爲例,現在用的不多,更多的是使用HashTable。

4.Special-purpose implementations. Implementations designed for use in special situations. These implementations display nonstandard performance characteristics, usage restrictions, or behavior.專用

5.Concurrent implementations. Implementations designed for highly concurrent use.專門針對於多線程的實現

6.Wrapper implementations. Add functionality, such as synchronization, to other implementations.

7.Convenience implementations. High-performance “mini-implementations” of the collection interfaces.

8.Abstract implementations. Partial implementations of the collection interfaces to facilitate custom implementations.實現了Collection接口的一部分功能,Skeletal implementations of the collection interfaces to facilitate custom implementations.

9.Algorithms. Static methods that perform useful functions on collections, such as sorting a list.例如Collections.sort()

10.Infrastructure. Interfaces that provide essential support for the collection interfaces.

11.Array Utilities. Utility functions for arrays of primitive types and reference objects. Not, strictly speaking, a part of the collections framework, this feature was added to the Java platform at the same time as the collections framework and relies on some of the same infrastructure.

你是否想問,上述這些部分都是由java中哪些類或者接口組成的呢,有沒有例子呢?答案在這裏Outline of the Collections Framework 。這對我們理解Collections framework的構成很重要,一定要認真看該參考答案!

Adapter implementations
感覺很高級,Implementations that adapt one collections interface to another:

1.newSetFromMap(Map) - Creates a general-purpose Set implementation from a general-purpose Map implementation.
2.asLifoQueue(Deque) - Returns a view of a Deque as a Last In First Out (LIFO) Queue.

Collection Interfaces

分爲兩部分,第一部分(也是最基本的)是在java.util.Collection中,他有下列的繼承者:

Set,SortedSet,NavigableSet,Queue,BlockingQueue,TranferQueue,Deque,BlockingDeque.

第二部分,是以java.util.Map爲基礎來實現的接口,並不是真正意義上的Collections。 但是,這些接口包含了collection-view的操作方式,也就是可以像操作Collections一樣操作Map。Map包含下列繼承者:

SortedMap,NavigableMap,ConcurrentMap,ConcurrentNavigableMap.

從源碼中也能看出有這兩部分:

//Collection
public interface Collection<E> extends Iterable<E> {}
//Set
public interface Set<E> extends Collection<E> {}
//List
public interface List<E> extends Collection<E> {}
//Map,單獨的一個接口
public interface Map<K,V> {}

Collection接口大部分方法都表明了optional(可選擇),意味着在具體實現的時候可以隨意選擇實現某些方法,如果調用沒有實現的方法,將拋出運行時錯誤UnsupportedOperationException。

Collection Implementations

The general purpose implementations(具有通用目的的實現) are summarized in the following table:

官方總結,值得信賴!

關於general purpose implementations:1.他們都支持Collection interface中的方法(隨意才叫通用嘛),2.對於存放在它們中的元素類型沒有任何限制,3.它們都不是線程安全的,但是如果你想要它們線程安全的版本的話,Collections有一個靜態工廠方法synchronizedCollection,可以改變它們爲線程安全的,具體用法還有一些額外要求,請參考synchronization wrappers

The AbstractCollection, AbstractSet, AbstractList, AbstractSequentialList and AbstractMap classes provide basic implementations of the core collection interfaces, to minimize the effort required to implement them. The API documentation for these classes describes precisely how each method is implemented so the implementer knows which methods must be overridden, given the performance of the basic operations of a specific implementation.

我想這些Abstract類的作用是方便我們自定義Collection

Concurrent Collections

多線程interface:

BlockingQueue,TransferQueue,BlockingDeque,ConcurrentMap,ConcurrentNavigableMap.

實現了的多線程的類:
LinkedBlockingQueue,ArrayBlockingQueue,PriorityBlockingQueue,DelayQueue,SynchronousQueue,LinkedBlockingDeque,LinkedTransferQueue,CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentSkipListSet,ConcurrentHashMap,ConcurrentSkipListMap.

Algorithms

The Collections class contains these useful static methods.Collections類包含了很多有用的靜態方法。

1.sort(List) - Sorts a list using a merge sort algorithm, which provides average case performance comparable to a high quality quicksort, guaranteed O(n*log n) performance (unlike quicksort), and stability (unlike quicksort). A stable sort is one that does not reorder equal elements.

2.binarySearch(List, Object) - Searches for an element in an ordered list using the binary search algorithm.

3.reverse(List) - Reverses the order of the elements in a list.

4.shuffle(List) - Randomly changes the order of the elements in a list.

5.fill(List, Object) - Overwrites every element in a list with the specified value.

6.copy(List dest, List src) - Copies the source list into the destination list.

7.min(Collection) - Returns the minimum element in a collection.

8.max(Collection) - Returns the maximum element in a collection.

9.rotate(List list, int distance) - Rotates all of the elements in the list by the specified distance.

10.replaceAll(List list, Object oldVal, Object newVal) - Replaces all occurrences of one specified value with another.

11.indexOfSubList(List source, List target) - Returns the index of the first sublist of source that is equal to target.

12.lastIndexOfSubList(List source, List target) - Returns the index of the last sublist of source that is equal to target.

13.swap(List, int, int) - Swaps the elements at the specified positions in the specified list.

14.frequency(Collection, Object) - Counts the number of times the specified element occurs in the specified collection.

15.disjoint(Collection, Collection) - Determines whether two collections are disjoint, in other words, whether they contain no elements in common.

16.addAll(Collection< super T>, T…) - Adds all of the elements in the specified array to the specified collection.

關於Comparable和Comparator

1.Comparable - Imparts a natural ordering to classes that implement it. The natural ordering can be used to sort a list or maintain order in a sorted set or map. Many classes were retrofitted to implement this interface.

2.Comparator - Represents an order relation, which can be used to sort a list or maintain order in a sorted set or map. Can override a type’s natural ordering or order objects of a type that does not implement the Comparable interface.作用有二:1.可以改變一個type的自然屬性2.對於沒有實現Comparable接口的type,可以讓他們可以被比較,從而有順序。

兩者比較

In short, there isn’t much difference. They are both ends to similar means. In general implement comparable for natural order, (natural order definition is obviously open to interpretation), and write a comparator for other sorting or comparison needs. 一個類實現了Comparable接口的話,那麼定義的肯定是它的最常用默認的排序方式嘛,因爲這樣將順序已經寫死了。而Comparator定義的順序並不影響該類的自然順序!

參考:Java : Comparable vs Comparator

Runtime exceptions

1.UnsupportedOperationException - Thrown by collections if an unsupported optional operation is called.

2.ConcurrentModificationException - Thrown by iterators and list iterators if the backing collection is changed unexpectedly while the iteration is in progress. Also thrown by sublist views of lists if the backing list is changed unexpectedly.

3-Tutorial

1.Lesson: Introduction to Collections

2.Lesson: Interfaces
鏈接,針對每個interface寫的都還行,推薦看看。interfaces總結

3.Lesson: Aggregate Operations

要明白本節的內容,首先需要了解Lambda ExpressionsMethod References。前面已經講過。

For what do you use collections? You don’t simply store objects in a collection and leave them there. In most cases, you use collections to retrieve items stored in them.

常規寫法:

for (Person p : roster) {
    System.out.println(p.getName());
}

用aggregate operation的寫法:

roster
    .stream()
    .forEach(e -> System.out.println(e.getName());

Pipelines and Streams

A pipeline is a sequence of aggregate operations.

A pipeline contains the following components:

1.A source: This could be a collection, an array, a generator function, or an I/O channel. In this example, the source is the collection roster.

2.Zero or more intermediate operations(中間操作). An intermediate operation, such as filter, produces a new stream.

A stream is a sequence of elements.

3.A terminal operation(最終操作). A terminal operation, such as forEach, produces a non-stream result, such as a primitive value (like a double value), a collection, or in the case of forEach, no value at all. In this example, the parameter of the forEach operation is the lambda expression e -> System.out.println(e.getName()), which invokes the method getName on the object e. (The Java runtime and compiler infer that the type of the object e is Person.)

示例:

The following example calculates the average age of all male members contained in the collection roster with a pipeline that consists of the aggregate operations filter, mapToInt, and average:

double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();

Differences Between Aggregate Operations and Iterators

Aggregate operations, like forEach, appear to be like iterators. However, they have several fundamental differences:

1.They use internal iteration
2.They process elements from a stream
3.They support behavior as parameters

更多的關於Aggregate Operations的內容:ReductionParallelism 。這應該是java自身的並行計算框架啊,跟Spark,Hadoop在並行計算方面的思想很相同。

4.Implementations

Summary of Implementations實現總結):

The Java Collections Framework provides several general-purpose implementations of the core interfaces:

1.For the Set interface, HashSet is the most commonly used implementation.
2.For the List interface, ArrayList is the most commonly used implementation.
3.For the Map interface, HashMap is the most commonly used implementation.
4.For the Queue interface, LinkedList is the most commonly used implementation.
5.For the Deque interface, ArrayDeque is the most commonly used implementation.

5.Lesson: Algorithms

它們都在Collections類中作爲靜態方法存在,大多數算法用在List上,還有一些用於任意的Collection實例上。

1.Sorting

The sort operation uses a slightly optimized merge sort algorithm that is fast and stable:

Fast: It is guaranteed to run in n log(n) time and runs substantially faster on nearly sorted lists. Empirical tests showed it to be as fast as a highly optimized quicksort. A quicksort is generally considered to be faster than a merge sort but isn’t stable and doesn’t guarantee n log(n) performance.(經過改善的merge sort具有stable,還能保證速度,速度方面跟quicksort差不多)

Stable: It doesn’t reorder equal elements. This is important if you sort the same list repeatedly on different attributes. If a user of a mail program sorts the inbox by mailing date and then sorts it by sender, the user naturally expects that the now-contiguous list of messages from a given sender will (still) be sorted by mailing date. This is guaranteed only if the second sort was stable.

2.Shuffling

shuffle

//swap
public static void swap(List<E> a, int i, int j) {
    E tmp = a.get(i);
    a.set(i, a.get(j));
    a.set(j, tmp);
}
//shuffle
public static void shuffle(List<?> list, Random rnd) {
    for (int i = list.size(); i > 1; i--)
        swap(list, i - 1, rnd.nextInt(i));
}

3.Routine Data Manipulation

manipulation:reverse,fill,copy,swap,addAll。

4.Searching

The binarySearch algorithm searches for a specified element in a sorted List.兩種方式調用之,一種是直接傳List和search key,但這需要保證List按照自然順序升序排列,另一種是再多傳一個Comparator,在search之前,先按Comparator順序排序。

5.Composition

composition:frequency,disjoint。

6.Lesson: Custom Collection Implementations

如果要自定義的話,這無疑是最好的官方指導

It is fairly easy to do this with the aid of the abstract implementations provided by the Java platform.

接下來我們來實現下Arrays.asList吧:

public static List<T> asList(T[] a) {
    return new MyArrayList<T>(a);
}

private static class MyArrayList<T> extends AbstractList<T> {

    private final T[] a;

    MyArrayList(T[] array) {
        a = array;
    }

    public T get(int index) {
        return a[index];
    }

    public T set(int index, T element) {
        T oldValue = a[index];
        a[index] = element;
        return oldValue;
    }

    public int size() {
        return a.length;
    }
}

4-Java Collections API Design FAQ

Q1:Won’t programmers have to surround any code that calls optional operations with a try-catch clause in case they throw an UnsupportedOperationException?

It was never our intention that programs should catch these exceptions: that’s why they’re unchecked (runtime) exceptions. They should only arise as a result of programming errors, in which case, your program will halt due to the uncaught exception.

Q2:Why don’t you provide an Iterator.add method?

The semantics are unclear, given that the contract for Iterator makes no guarantees about the order of iteration. Note, however, that ListIterator does provide an add operation, as it does guarantee the order of the iteration.

Q3:Why doesn’t Map extend Collection?

This was by design. We feel that mappings are not collections and collections are not mappings. Thus, it makes little sense for Map to extend the Collection interface (or vice versa).

If a Map is a Collection, what are the elements? The only reasonable answer is “Key-value pairs”, but this provides a very limited (and not particularly useful) Map abstraction. You can’t ask what value a given key maps to, nor can you delete the entry for a given key without knowing what value it maps to.

Collection could be made to extend Map, but this raises the question: what are the keys? There’s no really satisfactory answer, and forcing one leads to an unnatural interface.

Maps can be viewed as Collections (of keys, values, or pairs), and this fact is reflected in the three “Collection view operations” on Maps (keySet, entrySet, and values). While it is, in principle, possible to view a List as a Map mapping indices to elements, this has the nasty property that deleting an element from the List changes the Key associated with every element before the deleted element. That’s why we don’t have a map view operation on Lists.

6.java傳值方式

參考:Is Java “pass-by-reference” or “pass-by-value”?

highest vote 1:

Java is always pass-by-value. Unfortunately, they decided to call pointers references, thus confusing newbies. Because those references are passed by value.(因爲引用是通過值傳遞的)。

爲了證明是值傳遞還是引用傳遞,請看下面的代碼:

public static void main( String[] args ){
    Dog aDog = new Dog("Max");
    foo(aDog);

    if (aDog.getName().equals("Max")) { //true
        System.out.println( "Java passes by value." );

    } else if (aDog.getName().equals("Fifi")) {
        System.out.println( "Java passes by reference." );
    }
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true

    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

In this example aDog.getName() will still return “Max”. The value aDog within main is not overwritten in the function foo with the Dog “Fifi” as the object reference is passed by value. If it were passed by reference, then the aDog.getName() in main would return “Fifi” after the call to foo.(如果爲引用傳遞,那麼在call foo方法後,name會改變,但是並沒有!可見爲值傳遞)

再看下面代碼:

Dog aDog = new Dog("Max");
foo(aDog);
aDog.getName().equals("Fifi"); // true
public void foo(Dog d) {
    d.getName().equals("Max"); // true
    d.setName("Fifi");
}

In the above example, FiFi is the dog’s name after call to foo(aDog). Any operations that foo performs on d are such that, for all practical purposes, they are performed on aDog itself (except d=new Dog(“Boxer”)).

highest vote 2:

你可以簡單地認爲java傳值的方式是傳遞reference的內存地址(之所以這樣不夠準確是因爲java傳遞的不是直接地址(direct address),但差不多)。比如:

Dog myDog = new Dog("Rover");
foo(myDog);

you’re essentially passing the address of the created Dog object to the foo method.

(I say essentially because Java pointers aren’t direct addresses, but it’s easiest to think of them that way)

Suppose the Dog object resides at memory address 42. This means we pass 42 to the method.

Java works exactly like C. You can assign a pointer, pass the pointer to a method, follow the pointer in the method and change the data that was pointed to. However, you cannot change where that pointer points(即改變pointer指向的地址).

In C++, Ada, Pascal and other languages that support pass-by-reference, you can actually change the variable that was passed.

highest vote 3:

我們通過圖能更好地理解java的傳值方式。先看下面代碼:

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

下面按步驟來理解:

step1:
Declaring a reference named f of type Foo and assign it to a new object of type Foo with an attribute “f”.

Foo f = new Foo("f");

step2:
From the method side, a reference of type Foo with a name a is declared and it’s initially assigned to null.

public static void changeReference(Foo a)

step3:
As you call the method changeReference, the reference a will be assigned to the object which is passed as an argument.

changeReference(f);

step4:
Declaring a reference named b of type Foo and assign it to a new object of type Foo with an attribute “b”.

Foo b = new Foo("b");

step5:
a = b is re-assigning the reference a NOT f to the object whose its attribute is “b”.

step6:
As you call modifyReference(Foo c) method, a reference c is created and assigned to the object with attribute “f”.

step7:
c.setAttribute(“c”); will change the attribute of the object that reference c points to it, and it’s same object that reference f points to it.

hope you understand now how passing objects as arguments works in Java!

發佈了105 篇原創文章 · 獲贊 19 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章