/* Copyright 2009-2016 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package org.moeaframework.core; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** * A collection of solutions and common methods for manipulating the collection. */ public class Population implements Iterable<Solution> { /** * The internal data storage for solutions. */ private final List<Solution> data; /** * Constructs an empty population. */ public Population() { super(); data = new ArrayList<Solution>(); } /** * Constructs a population initialized with a collection of solutions. * * @param iterable the collection of solutions for initializing this * population */ public Population(Iterable<? extends Solution> iterable) { this(); addAll(iterable); } /** * Constructs a population initialized with an array of solutions. * * @param solutions the array of solutions for initializing this * population */ public <T extends Solution> Population(T[] solutions) { this(Arrays.asList(solutions)); } /** * Returns the solution at the specified index in this population. * * @param index the index of the solution to be returned * @return the solution at the specified index * @throws IndexOutOfBoundsException if the index is out of range * {@code (index < 0) || (index >= size())} */ public Solution get(int index) { return data.get(index); } /** * Removes the solution at the specified index from this population. * * @param index the index of the solution to be removed * @throws IndexOutOfBoundsException if the index is out of range * {@code (index < 0) || (index >= size())} */ public void remove(int index) { modCount++; data.remove(index); } /** * Returns the index of the specified solution in this population. * Invocations of certain methods on this population may alter the ordering * of solutions, so the index returned should be used immediately by the * {@code get} or {@code remove} methods. * * @param solution the solution whose index is to be returned * @return the index of the specified solution */ public int indexOf(Solution solution) { return data.indexOf(solution); } /** * Adds the specified solution to this population. * * @param solution the solution to be added * @return {@code true} if the population was modified as a result of this * method; {@code false} otherwise. */ public boolean add(Solution solution) { return data.add(solution); } /** * Adds a collection of solutions to this population. * * @param iterable the collection of solutions to be added * @return {@code true} if the population was modified as a result of this * method; {@code false} otherwise */ public boolean addAll(Iterable<? extends Solution> iterable) { boolean changed = false; for (Solution solution : iterable) { changed |= add(solution); } return changed; } /** * Adds an array of solutions to this population. * * @param solutions the solutions to be added * @return {@code true} if the population was modified as a result of this * method; {@code false} otherwise */ public <T extends Solution> boolean addAll(T[] solutions) { return addAll(Arrays.asList(solutions)); } /** * Replaces the solution at the given index. * * @param index the index to replace * @param solution the new solution */ public void replace(int index, Solution solution) { data.set(index, solution); } /** * Removes all solutions from this population. */ public void clear() { modCount++; data.clear(); } /** * Returns {@code true} if this population contains the specified solution; * {@code false} otherwise. * * @param solution the solution whose presence is tested * @return {@code true} if this population contains the specified * solution; {@code false} otherwise */ public boolean contains(Solution solution) { return data.contains(solution); } /** * Returns {@code true} if this population contains all the solutions in the * specified collection; {@code false} otherwise. * * @param iterable the collection whose presence is tested * @return {@code true} if this population contains all the solutions in the * specified collection; {@code false} otherwise */ public boolean containsAll(Iterable<? extends Solution> iterable) { boolean missing = false; for (Solution solution : iterable) { missing |= !contains(solution); } return !missing; } /** * Returns {@code true} if this population contains all the solutions in the * specified array; {@code false} otherwise. * * @param solutions the array whose presence is tested * @return {@code true} if this population contains all the solutions in the * specified array; {@code false} otherwise */ public <T extends Solution> boolean containsAll(T[] solutions) { return containsAll(Arrays.asList(solutions)); } /** * Returns {@code true} if this population contains no solutions; * {@code false} otherwise. * * @return {@code true} if this population contains no solutions; * {@code false} otherwise. */ public boolean isEmpty() { return data.isEmpty(); } /** * Returns an iterator for accessing the solutions in this population. */ @Override public Iterator<Solution> iterator() { return new PopulationIterator(); } /** * Removes the specified solution from this population, if present. * * @param solution the solution to be removed * @return {@code true} if this population was modified as a result of this * method; {@code false} otherwise */ public boolean remove(Solution solution) { modCount++; return data.remove(solution); } /** * Removes all solutions in the specified collection from this population. * * @param iterable the collection of solutions to be removed * @return {@code true} if this population was modified as a result of this * method; {@code false} otherwise */ public boolean removeAll(Iterable<? extends Solution> iterable) { boolean changed = false; for (Solution solution : iterable) { changed |= remove(solution); } return changed; } /** * Removes all solutions in the specified array from this population. * * @param solutions the array of solutions to be removed * @return {@code true} if this population was modified as a result of this * method; {@code false} otherwise */ public <T extends Solution> boolean removeAll(T[] solutions) { return removeAll(Arrays.asList(solutions)); } /** * Returns the number of solutions in this population. * * @return the number of solutions in this population */ public int size() { return data.size(); } /** * Sorts the solutions in this population using the specified comparator. * Invocations of certain methods on this population may alter the ordering * of solutions, so the {@code get}, {@code remove} and iteration methods * should be called immediately after invoking this method. * * @param comparator the comparator to be used for sorting */ public void sort(Comparator<? super Solution> comparator) { Collections.sort(data, comparator); } /** * Sorts this population using the specified comparator and removes the last * (maximum) solutions until this population's size is within the specified * size. * * @param size the target population size after truncation * @param comparator the comparator to be used for truncation */ public void truncate(int size, Comparator<? super Solution> comparator) { sort(comparator); while (data.size() > size) { data.remove(data.size() - 1); } } /* * The following code is based on the Apache Commons Collections library. * This is to provide a similar iterator behavior to other collection * classes without requiring the Population to implement all collection * methods. The license terms are provided below. * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * The modification count. */ private int modCount; /** * An iterator over the solutions in a population. */ private class PopulationIterator implements Iterator<Solution> { /** * The index of the next node to be returned. */ private int nextIndex; /** * The index of the last node that was returned. Set to {@code -1} * if the iterator is not positioned at a valid node (i.e., at * initialization or after an element is removed). */ private int currentIndex; /** * The modification count that the list is expected to have. If the list * doesn't have this count, then a * {@link java.util.ConcurrentModificationException} may be thrown by * the operations. */ private int expectedModCount; /** * Constructs a population iterator. */ public PopulationIterator() { super(); nextIndex = 0; currentIndex = -1; expectedModCount = modCount; } @Override public boolean hasNext() { return nextIndex != size(); } @Override public Solution next() { checkModCount(); if (!hasNext()) { throw new NoSuchElementException(); } try { Solution value = get(nextIndex); currentIndex = nextIndex++; return value; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } @Override public void remove() { checkModCount(); if (currentIndex == -1) { throw new IllegalStateException(); } try { Population.this.remove(currentIndex); nextIndex--; currentIndex = -1; expectedModCount++; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } /** * Checks the modification count of the list is the value that this * object expects. * * @throws ConcurrentModificationException if the list's modification * count is not the value that was expected */ private void checkModCount() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } } }