# Performance of Parallel K-Means Algorithms in Java
## 3 Parallel K-Means in Java
### Supporting K-Means by Streams  
A first solution to host parallel K-Means in Java is based on the use of streams, lambda expressions and a functional programming style [12], which were introduced since the Java 8 version. Streams are _views_ (not copies) of collections (e.g., lists) of objects, which make it possible to express a fluent style of operations (method calls). Each operation works on a stream, transforms each object according to a lambda expression, and returns a new stream, ready to be handled by a new operation and so forth. In a fluent code segment, only the execution of the terminal operation actually triggers the execution of the intermediate operations. Figure 4 depicts the main part of the K-Means solution based on streams which can execute either serially or in parallel. The two modes are controlled by a global Boolean parameter \(PARALLEL\) which can be \(false\) or \(true\). The algorithm first loads the dataset and the initial centroids onto two global arrays of data points (of a class \(DataPoint\)), respectively \(dataset\) and \(centroids\) of length \(N\) and \(K\), from which corresponding (possibly parallel) streams are built. A Map /Reduce schema was actually coded where the Map stage corresponds to the \(Assign\) step of Figure 1, and the Reduce stage realizes the \(Update\) step of Figure 1.  
The \(DataPoint\) class describes a point in \(R^{D}\), that is with \(D\) coordinates, and exposes methods for calculating the Euclidean distance between two points, adding points (by adding the corresponding coordinates and counting the number of additions), calculating the mean of a point summation and so forth. \(DataPoint\) also admits a field \(CID\) (Cluster Identifier) which stores the \(ID\) (index of the \(centroids\) array) of the cluster the point belongs to. The \(CID\) field is handled by the \(getCID()/setCID()\) methods. The \(map()\) method on \(p\_stream\) receives a lambda expression which accepts a \(DataPoint\)\(p\) and returns \(p\) mapped to the cluster centroid closer to \(p\). Since \(map()\) is an intermediate operation, a final fictitious \(forEach()\) operation is added which activates the execution of \(map()\). When \(p\_stream\) is completely processed, a \(newCentroids\) array is reset (each new centroid has 0 as coordinates) so as to generate on it the updated version of centroids. Purposely, the \(CID\) field of every new centroid is set to itself. The \(c\_stream\) is actually extracted from the \(newCentroids\) array. The \(map()\) method on \(c\_stream\) receives a lambda expression which accepts a new centroid point \(c\) and adds to \(c\) all the points of the dataset whose \(CID\) coincides with the \(CID\) of \(c\). Following the summation of the points belonging to the same new centroid \(c\), the \(mean()\) method is invoked on \(c\) to calculate the mean point of the resultant cluster. The iteration counter \(it\) gets incremented at the end of each K-Means iteration. The \(termination()\) method returns \(true\) when the convergence was obtained or the maximum number of iterations \(T\) was reached. In any case, \(termination()\) ends by copying the contents of \(newCentroids\) onto the \(centroids\) array. Convergence is sensed when all the distances among new centroids and current centroids fall under the threshold \(THR\).