From 9984d4be42b6a4eacaa00eb0c800e6f876608815 Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 11 May 2020 21:00:42 +0100 Subject: [PATCH] Optimize ActionLog and Paginated util --- .../luckperms/api/platform/PlayerAdapter.java | 1 + .../common/actionlog/ActionComparator.java | 96 +++++++++++++++++++ .../lucko/luckperms/common/actionlog/Log.java | 34 +++---- .../common/actionlog/LoggedAction.java | 20 +--- .../common/commands/log/LogGroupHistory.java | 22 ++--- .../common/commands/log/LogRecent.java | 22 ++--- .../common/commands/log/LogSearch.java | 20 ++-- .../common/commands/log/LogTrackHistory.java | 22 ++--- .../common/commands/log/LogUserHistory.java | 22 ++--- .../lucko/luckperms/common/sender/Sender.java | 2 +- .../common/util/ImmutableCollectors.java | 9 ++ .../luckperms/common/util/Paginated.java | 56 ++++++----- 12 files changed, 203 insertions(+), 123 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/common/actionlog/ActionComparator.java diff --git a/api/src/main/java/net/luckperms/api/platform/PlayerAdapter.java b/api/src/main/java/net/luckperms/api/platform/PlayerAdapter.java index 9164b2fc4..8ac5c6844 100644 --- a/api/src/main/java/net/luckperms/api/platform/PlayerAdapter.java +++ b/api/src/main/java/net/luckperms/api/platform/PlayerAdapter.java @@ -100,6 +100,7 @@ public interface PlayerAdapter { default @NonNull CachedPermissionData getPermissionData(@NonNull T player) { return getUser(player).getCachedData().getPermissionData(getQueryOptions(player)); } + /** * Gets the current {@link CachedMetaData} for the {@code player}, * using their {@link #getQueryOptions(Object) active query options}. diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/ActionComparator.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/ActionComparator.java new file mode 100644 index 000000000..77093a13b --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/ActionComparator.java @@ -0,0 +1,96 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.actionlog; + +import net.luckperms.api.actionlog.Action; + +import java.util.Comparator; +import java.util.Optional; + +final class ActionComparator implements Comparator { + public static final Comparator INSTANCE = new ActionComparator(); + + private ActionComparator() { + + } + + @Override + public int compare(Action o1, Action o2) { + int cmp = o1.getTimestamp().compareTo(o2.getTimestamp()); + if (cmp != 0) { + return cmp; + } + + Action.Source o1Source = o1.getSource(); + Action.Source o2Source = o2.getSource(); + + cmp = o1Source.getUniqueId().compareTo(o2Source.getUniqueId()); + if (cmp != 0) { + return cmp; + } + + cmp = o1Source.getName().compareTo(o2Source.getName()); + if (cmp != 0) { + return cmp; + } + + Action.Target o1Target = o1.getTarget(); + Action.Target o2Target = o2.getTarget(); + + cmp = o1Target.getType().compareTo(o2Target.getType()); + if (cmp != 0) { + return cmp; + } + + cmp = o1Target.getType().compareTo(o2Target.getType()); + if (cmp != 0) { + return cmp; + } + + cmp = compareOptionals(o1Target.getUniqueId(), o2Target.getUniqueId()); + if (cmp != 0) { + return cmp; + } + + cmp = o1Source.getName().compareTo(o2Source.getName()); + if (cmp != 0) { + return cmp; + } + + return o1.getDescription().compareTo(o2.getDescription()); + } + + @SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "OptionalIsPresent"}) + private static > int compareOptionals(Optional a, Optional b) { + if (!a.isPresent()) { + return b.isPresent() ? -1 : 0; + } else if (!b.isPresent()) { + return 1; + } else { + return a.get().compareTo(b.get()); + } + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java index c423a8e84..e72df5d4c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java @@ -25,36 +25,33 @@ package me.lucko.luckperms.common.actionlog; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedSet; import me.lucko.luckperms.common.util.ImmutableCollectors; import net.luckperms.api.actionlog.Action; -import java.util.Comparator; +import java.util.ArrayList; +import java.util.List; import java.util.SortedSet; -import java.util.TreeSet; import java.util.UUID; -import java.util.stream.Collectors; public class Log { - private static Log empty = null; + private static final Log EMPTY = new Log(ImmutableList.of()); public static Builder builder() { return new Builder(); } - public static synchronized Log empty() { - if (empty == null) { - empty = builder().build(); - } - return empty; + public static Log empty() { + return EMPTY; } private final SortedSet content; - public Log(SortedSet content) { - this.content = ImmutableSortedSet.copyOfSorted(content); + Log(List content) { + this.content = ImmutableSortedSet.copyOf(content); } public SortedSet getContent() { @@ -64,38 +61,38 @@ public class Log { public SortedSet getContent(UUID actor) { return this.content.stream() .filter(e -> e.getSource().getUniqueId().equals(actor)) - .collect(Collectors.toCollection(TreeSet::new)); + .collect(ImmutableCollectors.toSortedSet()); } public SortedSet getUserHistory(UUID uniqueId) { return this.content.stream() .filter(e -> e.getTarget().getType() == Action.Target.Type.USER) .filter(e -> e.getTarget().getUniqueId().isPresent() && e.getTarget().getUniqueId().get().equals(uniqueId)) - .collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); + .collect(ImmutableCollectors.toSortedSet()); } public SortedSet getGroupHistory(String name) { return this.content.stream() .filter(e -> e.getTarget().getType() == Action.Target.Type.GROUP) .filter(e -> e.getTarget().getName().equals(name)) - .collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); + .collect(ImmutableCollectors.toSortedSet()); } public SortedSet getTrackHistory(String name) { return this.content.stream() .filter(e -> e.getTarget().getType() == Action.Target.Type.TRACK) .filter(e -> e.getTarget().getName().equals(name)) - .collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); + .collect(ImmutableCollectors.toSortedSet()); } public SortedSet getSearch(String query) { return this.content.stream() .filter(e -> e.matchesSearch(query)) - .collect(ImmutableCollectors.toSortedSet(Comparator.naturalOrder())); + .collect(ImmutableCollectors.toSortedSet()); } public static class Builder { - private final SortedSet content = new TreeSet<>(); + private final List content = new ArrayList<>(); public Builder add(LoggedAction e) { this.content.add(e); @@ -103,6 +100,9 @@ public class Log { } public Log build() { + if (this.content.isEmpty()) { + return EMPTY; + } return new Log(this.content); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java index 60af9605b..579f0f5ad 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/LoggedAction.java @@ -45,7 +45,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -58,15 +57,6 @@ import java.util.UUID; */ public class LoggedAction implements Action { - private static final Comparator COMPARATOR = Comparator - .comparingLong(a -> a.getTimestamp().getEpochSecond()) - .thenComparing(a -> a.getSource().getUniqueId()) - .thenComparing(a -> a.getSource().getName(), String.CASE_INSENSITIVE_ORDER) - .thenComparing(a -> a.getTarget().getType()) - .thenComparing(e -> e.getTarget().getUniqueId().map(UUID::toString).orElse("")) - .thenComparing(a -> a.getTarget().getName(), String.CASE_INSENSITIVE_ORDER) - .thenComparing(Action::getDescription); - /** * Creates a new log entry builder * @@ -131,7 +121,7 @@ public class LoggedAction implements Action { @Override public int compareTo(@NonNull Action other) { Objects.requireNonNull(other, "other"); - return COMPARATOR.compare(this, other); + return ActionComparator.INSTANCE.compare(this, other); } public boolean matchesSearch(String query) { @@ -171,13 +161,7 @@ public class LoggedAction implements Action { @Override public int hashCode() { - final int PRIME = 59; - int result = 1; - result = result * PRIME + getTimestamp().hashCode(); - result = result * PRIME + getSource().hashCode(); - result = result * PRIME + getTarget().hashCode(); - result = result * PRIME + getDescription().hashCode(); - return result; + return Objects.hash(getTimestamp(), getSource(), getTarget(), getDescription()); } private static final class SourceImpl implements Source { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java index ed9c14e92..108511f46 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogGroupHistory.java @@ -43,11 +43,7 @@ import me.lucko.luckperms.common.util.DurationFormatter; import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Predicates; -import net.luckperms.api.actionlog.Action; - import java.util.List; -import java.util.Map; -import java.util.SortedMap; public class LogGroupHistory extends ChildCommand { private static final int ENTRIES_PER_PAGE = 10; @@ -90,18 +86,18 @@ public class LogGroupHistory extends ChildCommand { return CommandResult.INVALID_ARGS; } - SortedMap entries = log.getPage(page, ENTRIES_PER_PAGE); - String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); + List> entries = log.getPage(page, ENTRIES_PER_PAGE); + String name = entries.stream().findAny().get().value().getTarget().getName(); Message.LOG_HISTORY_GROUP_HEADER.send(sender, name, page, maxPage); - for (Map.Entry e : entries.entrySet()) { + for (Paginated.Entry e : entries) { Message.LOG_ENTRY.send(sender, - e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), - e.getValue().getSourceFriendlyString(), - Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), - e.getValue().getTargetFriendlyString(), - e.getValue().getDescription() + e.position(), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()), + e.value().getSourceFriendlyString(), + Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())), + e.value().getTargetFriendlyString(), + e.value().getDescription() ); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java index b4f8eb449..4630988e9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogRecent.java @@ -40,11 +40,7 @@ import me.lucko.luckperms.common.util.DurationFormatter; import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Predicates; -import net.luckperms.api.actionlog.Action; - import java.util.List; -import java.util.Map; -import java.util.SortedMap; import java.util.UUID; public class LogRecent extends ChildCommand { @@ -95,9 +91,9 @@ public class LogRecent extends ChildCommand { return CommandResult.INVALID_ARGS; } - SortedMap entries = log.getPage(page, ENTRIES_PER_PAGE); + List> entries = log.getPage(page, ENTRIES_PER_PAGE); if (specificUser) { - String name = ((Action) entries.values().stream().findAny().get()).getSource().getName(); + String name = entries.stream().findAny().get().value().getSource().getName(); if (name.contains("@")) { name = name.split("@")[0]; } @@ -106,14 +102,14 @@ public class LogRecent extends ChildCommand { Message.LOG_RECENT_HEADER.send(sender, page, maxPage); } - for (Map.Entry e : entries.entrySet()) { + for (Paginated.Entry e : entries) { Message.LOG_ENTRY.send(sender, - e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), - e.getValue().getSourceFriendlyString(), - Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), - e.getValue().getTargetFriendlyString(), - e.getValue().getDescription() + e.position(), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()), + e.value().getSourceFriendlyString(), + Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())), + e.value().getTargetFriendlyString(), + e.value().getDescription() ); } return CommandResult.SUCCESS; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java index 3bf48e07e..2a9ee877f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogSearch.java @@ -39,11 +39,7 @@ import me.lucko.luckperms.common.util.DurationFormatter; import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Predicates; -import net.luckperms.api.actionlog.Action; - import java.util.List; -import java.util.Map; -import java.util.SortedMap; public class LogSearch extends ChildCommand { private static final int ENTRIES_PER_PAGE = 10; @@ -90,17 +86,17 @@ public class LogSearch extends ChildCommand { return CommandResult.INVALID_ARGS; } - SortedMap entries = log.getPage(page, ENTRIES_PER_PAGE); + List> entries = log.getPage(page, ENTRIES_PER_PAGE); Message.LOG_SEARCH_HEADER.send(sender, query, page, maxPage); - for (Map.Entry e : entries.entrySet()) { + for (Paginated.Entry e : entries) { Message.LOG_ENTRY.send(sender, - e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), - e.getValue().getSourceFriendlyString(), - Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), - e.getValue().getTargetFriendlyString(), - e.getValue().getDescription() + e.position(), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()), + e.value().getSourceFriendlyString(), + Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())), + e.value().getTargetFriendlyString(), + e.value().getDescription() ); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java index 16ac6aa04..0029eef0f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogTrackHistory.java @@ -43,11 +43,7 @@ import me.lucko.luckperms.common.util.DurationFormatter; import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Predicates; -import net.luckperms.api.actionlog.Action; - import java.util.List; -import java.util.Map; -import java.util.SortedMap; public class LogTrackHistory extends ChildCommand { private static final int ENTRIES_PER_PAGE = 10; @@ -90,18 +86,18 @@ public class LogTrackHistory extends ChildCommand { return CommandResult.INVALID_ARGS; } - SortedMap entries = log.getPage(page, ENTRIES_PER_PAGE); - String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); + List> entries = log.getPage(page, ENTRIES_PER_PAGE); + String name = entries.stream().findAny().get().value().getTarget().getName(); Message.LOG_HISTORY_TRACK_HEADER.send(sender, name, page, maxPage); - for (Map.Entry e : entries.entrySet()) { + for (Paginated.Entry e : entries) { Message.LOG_ENTRY.send(sender, - e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), - e.getValue().getSourceFriendlyString(), - Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), - e.getValue().getTargetFriendlyString(), - e.getValue().getDescription() + e.position(), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()), + e.value().getSourceFriendlyString(), + Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())), + e.value().getTargetFriendlyString(), + e.value().getDescription() ); } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java index 4e29d59ee..44c1fee83 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/log/LogUserHistory.java @@ -40,11 +40,7 @@ import me.lucko.luckperms.common.util.DurationFormatter; import me.lucko.luckperms.common.util.Paginated; import me.lucko.luckperms.common.util.Predicates; -import net.luckperms.api.actionlog.Action; - import java.util.List; -import java.util.Map; -import java.util.SortedMap; import java.util.UUID; public class LogUserHistory extends ChildCommand { @@ -83,18 +79,18 @@ public class LogUserHistory extends ChildCommand { return CommandResult.INVALID_ARGS; } - SortedMap entries = log.getPage(page, ENTRIES_PER_PAGE); - String name = ((Action) entries.values().stream().findAny().get()).getTarget().getName(); + List> entries = log.getPage(page, ENTRIES_PER_PAGE); + String name = entries.stream().findAny().get().value().getTarget().getName(); Message.LOG_HISTORY_USER_HEADER.send(sender, name, page, maxPage); - for (Map.Entry e : entries.entrySet()) { + for (Paginated.Entry e : entries) { Message.LOG_ENTRY.send(sender, - e.getKey(), - DurationFormatter.CONCISE_LOW_ACCURACY.format(e.getValue().getDurationSince()), - e.getValue().getSourceFriendlyString(), - Character.toString(LoggedAction.getTypeCharacter(((Action) e.getValue()).getTarget().getType())), - e.getValue().getTargetFriendlyString(), - e.getValue().getDescription() + e.position(), + DurationFormatter.CONCISE_LOW_ACCURACY.format(e.value().getDurationSince()), + e.value().getSourceFriendlyString(), + Character.toString(LoggedAction.getTypeCharacter(e.value().getTarget().getType())), + e.value().getTargetFriendlyString(), + e.value().getDescription() ); } diff --git a/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java b/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java index 5c616326b..f5f486a5b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java +++ b/common/src/main/java/me/lucko/luckperms/common/sender/Sender.java @@ -45,7 +45,7 @@ import java.util.stream.Collectors; public interface Sender { /** The uuid used by the console sender. */ - UUID CONSOLE_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + UUID CONSOLE_UUID = new UUID(0, 0); // 00000000-0000-0000-0000-000000000000 /** The name used by the console sender. */ String CONSOLE_NAME = "Console"; diff --git a/common/src/main/java/me/lucko/luckperms/common/util/ImmutableCollectors.java b/common/src/main/java/me/lucko/luckperms/common/util/ImmutableCollectors.java index e685465c6..193ec94f2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/ImmutableCollectors.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/ImmutableCollectors.java @@ -72,6 +72,15 @@ public final class ImmutableCollectors { ); } + public static > Collector> toSortedSet() { + return Collector.of( + ImmutableSortedSet::naturalOrder, + ImmutableSortedSet.Builder::add, + (l, r) -> l.addAll(r.build()), + ImmutableSortedSet.Builder::build + ); + } + public static Collector> toSortedSet(Comparator comparator) { return Collector.of( () -> new ImmutableSortedSet.Builder(comparator), diff --git a/common/src/main/java/me/lucko/luckperms/common/util/Paginated.java b/common/src/main/java/me/lucko/luckperms/common/util/Paginated.java index 4508c5fe3..2523b6172 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/Paginated.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/Paginated.java @@ -27,11 +27,9 @@ package me.lucko.luckperms.common.util; import com.google.common.collect.ImmutableList; +import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.stream.Stream; /** * A simple pagination utility @@ -45,10 +43,6 @@ public class Paginated { this.content = ImmutableList.copyOf(content); } - public Paginated(Stream content) { - this.content = content.collect(ImmutableCollectors.toList()); - } - public List getContent() { return this.content; } @@ -57,32 +51,48 @@ public class Paginated { return (int) Math.ceil((double) this.content.size() / (double) entriesPerPage); } - public SortedMap getPage(int pageNo, int entries) { + public List> getPage(int pageNo, int pageSize) { if (pageNo < 1) { throw new IllegalArgumentException("pageNo cannot be less than 1: " + pageNo); } - int minimumEntries = ((pageNo * entries) - entries) + 1; - if (this.content.size() < minimumEntries) { - throw new IllegalStateException("Content does not contain that many elements. " + - "Requested: " + minimumEntries + ", Size: " + this.content.size()); + int first = ((pageNo - 1) * pageSize); + if (this.content.size() <= first) { + throw new IllegalStateException("Content does not contain that many elements. (requested page: " + pageNo + + ", page size: " + pageSize + ", page first index: " + first + ", content size: " + this.content.size() + ")"); } - final SortedMap out = new TreeMap<>(); + int last = first + pageSize - 1; + List> out = new ArrayList<>(pageSize); - final int max = minimumEntries + entries - 1; - int index = 0; - for (T e : this.content) { - index++; - if (index >= minimumEntries) { - out.put(index, e); - } - if (index == max) { - break; - } + for (int i = first; i <= last && i < this.content.size(); i++) { + out.add(new Entry<>(i + 1, this.content.get(i))); } return out; } + public static final class Entry { + private final int position; + private final T value; + + public Entry(int position, T value) { + this.position = position; + this.value = value; + } + + public int position() { + return this.position; + } + + public T value() { + return this.value; + } + + @Override + public String toString() { + return this.position + ": " + this.value; + } + } + }